Repository: prometheus-lua/Prometheus
Branch: master
Commit: 9d90158b54f4
Files: 124
Total size: 391.9 KB
Directory structure:
gitextract_h0mah7x0/
├── .editorconfig
├── .gitattributes
├── .gitbook.yaml
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── workflows/
│ ├── Build.yml
│ └── Test.yml
├── .gitignore
├── LICENSE
├── benchmark.lua
├── cli.lua
├── doc/
│ ├── README.md
│ ├── SUMMARY.md
│ ├── advanced/
│ │ └── using-prometheus-in-your-lua-application.md
│ ├── getting-started/
│ │ ├── command-line-options.md
│ │ ├── installation.md
│ │ ├── obfuscating-your-first-script.md
│ │ ├── presets.md
│ │ ├── the-config-object.md
│ │ └── writing-a-custom-config-file.md
│ └── steps/
│ ├── anti-tamper.md
│ ├── constantarray.md
│ ├── encryptstrings.md
│ ├── proxifylocals.md
│ ├── splitstrings.md
│ ├── vmify.md
│ └── wrapinfunction.md
├── prometheus-main.lua
├── readme.md
├── src/
│ ├── cli.lua
│ ├── colors.lua
│ ├── highlightlua.lua
│ ├── logger.lua
│ ├── presets.lua
│ ├── prometheus/
│ │ ├── ast.lua
│ │ ├── compiler/
│ │ │ ├── block.lua
│ │ │ ├── compile_core.lua
│ │ │ ├── compile_top.lua
│ │ │ ├── compiler.lua
│ │ │ ├── constants.lua
│ │ │ ├── emit.lua
│ │ │ ├── expressions/
│ │ │ │ ├── and.lua
│ │ │ │ ├── binary.lua
│ │ │ │ ├── boolean.lua
│ │ │ │ ├── function_call.lua
│ │ │ │ ├── function_literal.lua
│ │ │ │ ├── index.lua
│ │ │ │ ├── len.lua
│ │ │ │ ├── negate.lua
│ │ │ │ ├── nil.lua
│ │ │ │ ├── not.lua
│ │ │ │ ├── number.lua
│ │ │ │ ├── or.lua
│ │ │ │ ├── pass_self_function_call.lua
│ │ │ │ ├── string.lua
│ │ │ │ ├── table_constructor.lua
│ │ │ │ ├── vararg.lua
│ │ │ │ └── variable.lua
│ │ │ ├── expressions.lua
│ │ │ ├── register.lua
│ │ │ ├── statements/
│ │ │ │ ├── assignment.lua
│ │ │ │ ├── break_statement.lua
│ │ │ │ ├── compound.lua
│ │ │ │ ├── continue_statement.lua
│ │ │ │ ├── do_statement.lua
│ │ │ │ ├── for_in_statement.lua
│ │ │ │ ├── for_statement.lua
│ │ │ │ ├── function_call.lua
│ │ │ │ ├── function_declaration.lua
│ │ │ │ ├── if_statement.lua
│ │ │ │ ├── local_function_declaration.lua
│ │ │ │ ├── local_variable_declaration.lua
│ │ │ │ ├── pass_self_function_call.lua
│ │ │ │ ├── repeat_statement.lua
│ │ │ │ ├── return.lua
│ │ │ │ └── while_statement.lua
│ │ │ ├── statements.lua
│ │ │ └── upvalue.lua
│ │ ├── enums.lua
│ │ ├── namegenerators/
│ │ │ ├── Il.lua
│ │ │ ├── confuse.lua
│ │ │ ├── mangled.lua
│ │ │ ├── mangled_shuffled.lua
│ │ │ └── number.lua
│ │ ├── namegenerators.lua
│ │ ├── parser.lua
│ │ ├── pipeline.lua
│ │ ├── randomLiterals.lua
│ │ ├── randomStrings.lua
│ │ ├── scope.lua
│ │ ├── step.lua
│ │ ├── steps/
│ │ │ ├── AddVararg.lua
│ │ │ ├── AntiTamper.lua
│ │ │ ├── ConstantArray.lua
│ │ │ ├── EncryptStrings.lua
│ │ │ ├── NumbersToExpressions.lua
│ │ │ ├── ProxifyLocals.lua
│ │ │ ├── SplitStrings.lua
│ │ │ ├── Vmify.lua
│ │ │ ├── Watermark.lua
│ │ │ ├── WatermarkCheck.lua
│ │ │ └── WrapInFunction.lua
│ │ ├── steps.lua
│ │ ├── tokenizer.lua
│ │ ├── unparser.lua
│ │ ├── util.lua
│ │ └── visitast.lua
│ └── prometheus.lua
├── tests/
│ ├── ambiguous-call.lua
│ ├── closures.lua
│ ├── coroutines.lua
│ ├── fibonacci.lua
│ ├── iterator.lua
│ ├── loops.lua
│ ├── matrix.lua
│ ├── metatables.lua
│ ├── multi-return.lua
│ ├── primes.lua
│ ├── repeat-test.lua
│ ├── state-machine.lua
│ ├── strings.lua
│ ├── syntax.lua
│ ├── table-merge.lua
│ └── upvalues.lua
└── tests.lua
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
[*.lua]
indent_style = tab
indent_size = 4
end_of_line = lf
================================================
FILE: .gitattributes
================================================
*.lua text=auto
*.lua eol=lf
================================================
FILE: .gitbook.yaml
================================================
root: ./doc/
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**To Reproduce**
Steps to reproduce the behavior:
If your problem is a non-working obfuscated file, please also include a minimal source code example, your config file as well as the output file that you got.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/workflows/Build.yml
================================================
name: Build
on:
push:
branches:
main
jobs:
build:
runs-on: windows-latest # gh-actions-lua doesn't work on windows
steps:
- name: Checkout repo
uses: actions/checkout@master
- name: Download srlua-mingw
run: curl -o srlua.zip "https://raw.githubusercontent.com/joedf/LuaBuilds/gh-pages/hdata/srlua-5.1.5_Win32_bin.zip"
- name: Unzip srlua-mingw
run: |
tar -xf srlua.zip
Rename-Item -Path srglue.exe -NewName glue.exe
- name: Download Lua53
run: curl -o Lua53.zip "https://raw.githubusercontent.com/joedf/LuaBuilds/gh-pages/hdata/lua-5.3.5_Win64_bin.zip"
- name: Unzip Lua53
run: |
tar -xf Lua53.zip
- run: dir
- name: Build the project
run: |
./build.bat
shell: bash
- name: Zip the build
uses: papeloto/action-zip@v1
with:
files: build/
dest: build.zip
- name: Load version and name
run: | # I have no idea why but 5.1 just won't work for some reason
echo "prometheus_full_version=$(./lua.exe ./src/config.lua --FullVersion)" >> $GITHUB_ENV
echo "prometheus_version=$(./lua.exe ./src/config.lua --Version)" >> $GITHUB_ENV
shell: bash
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build.zip
asset_name: ${{ env.prometheus_full_version }}.zip
tag: release-${{ github.ref }}-${{ env.prometheus_version }}
release_name: ${{ env.prometheus_version }}
overwrite: true
body: ${{ env.prometheus_full_version }} ${{ github.event.commits[0].message }}
================================================
FILE: .github/workflows/Test.yml
================================================
name: Test
on:
push:
pull_request:
branches:
- master
jobs:
test-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@master
- name: Install Lua
uses: leafo/gh-actions-lua@master
with:
luaVersion: 5.1
- name: Run test case
run: lua ./tests.lua --Linux --CI
================================================
FILE: .gitignore
================================================
# Ignore Vscode Folder
.vscode
# Ignore Lua Implementation and srlua
lua51.dll
luajit.exe
srlua
luajit
buildnotes.txt
srlua.exe
glue.exe
build
# Ignore Local Test Files
test.lua
test.obfuscated.lua
config.lua
================================================
FILE: LICENSE
================================================
PROMETHEUS LICENSE
Copyright (c) 2025 Elias Oelschner
https://github.com/prometheus-lua/Prometheus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Retained Notice.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software that are distributed
in source or object form, unless explicitly stated otherwise in this
license.
2. Attribution Requirement.
Any Product or Service that (a) includes, links to, wraps, depends on,
is derived from, uses, or otherwise incorporates the Software (including
via modification, obfuscation, binary linking, packaging, or embedding),
whether distributed, provided for use, or made available over a network
(collectively, "Using Products/Services"), must provide clear,
reasonably prominent attribution to Prometheus as follows:
"Based on Prometheus by Elias Oelschner, https://github.com/prometheus-lua/Prometheus"
This attribution must appear in at least one of the following locations,
as applicable and reasonably prominent for the type of Using Product/Service:
a) In-user interface: a visible "About", "Credits", or equivalent screen
within the product's primary user interface (graphical or web UI).
b) On the product web site: a visible place such as a footer, About page,
or documentation landing page that is accessible to the product's users.
c) In the tool itself: the program's --version, --help output, or similar
command-line/displayed metadata for CLI tools.
d) Documentation: in user or developer documentation, README, or product
manual that accompanies the product.
For Software provided as a Network Service or SaaS (i.e., made available
for use over a network rather than distributed as a copy), the attribution
must be visible on the public-facing web UI (for example on the login
screen, footer, About page, or a help/credits page accessible without an
account), and must include the exact phrase above and the URL.
3. Derivative Statement.
Any derivative work, modification, fork, or reimplementation of the
Software that is publicly distributed, publicly hosted, or provided as a
service must include a prominent statement in its README (or equivalent
public-facing documentation) that it is based on Prometheus and include the
exact attribution text in section 2.
4. Generated Output Exception.
Files or artifacts produced by running or using the Software (for example,
obfuscated output files, compiled artifacts, transformed source files,
or other generated outputs) are NOT required by this license to carry
copyright notices or license text. The attribution obligations in section 2
continue to apply to the Using Product/Service that produces, distributes,
or serves those outputs.
5. No Trademark License.
This license does not grant any rights to use the trade names, trademarks,
service marks, or product names of the Licensor except as required for the
textual attribution specified in section 2.
6. Compliance and Termination.
Any failure to comply with the obligations in sections 2 or 3 will
terminate the rights granted under this license for the non-compliant
party with respect to the Software. Rights may be reinstated if the
non-compliant party cures the breach within thirty (30) days after
receiving written notice from the Licensor and provides reasonable proof
of cure.
7. Sublicensing.
You may sublicense the Software, provided that any sublicensee is bound
by the terms of this license and the attribution obligations described
herein.
8. Warranty Disclaimer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9. Limitation of Liability.
IN NO EVENT WILL THE COPYRIGHT HOLDER BE LIABLE FOR ANY INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY
WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
10. Governing Law.
This license shall be governed by and construed in accordance with the
laws of the jurisdiction chosen by the Licensor. This clause does not
limit any mandatory consumer protections that may apply in certain
jurisdictions.
By exercising any rights granted to you under this License, you accept and
agree to be bound by the terms and conditions of this License.
================================================
FILE: benchmark.lua
================================================
print("PROMETHEUS Benchmark")
print("Based On IronBrew Benchmark")
local Iterations = 100000
print("Iterations: " .. tostring(Iterations))
print("CLOSURE testing.")
local Start = os.clock()
local TStart = Start
for _ = 1, Iterations do
(function()
if not true then
print("Hey gamer.")
end
end)()
end
print("Time:", os.clock() - Start .. "s")
print("SETTABLE testing.")
Start = os.clock()
local T = {}
for Idx = 1, Iterations do
T[tostring(Idx)] = "EPIC GAMER " .. tostring(Idx)
end
print("Time:", os.clock() - Start .. "s")
print("GETTABLE testing.")
Start = os.clock()
for Idx = 1, Iterations do
T[1] = T[tostring(Idx)]
end
print("Time:", os.clock() - Start .. "s")
print("Total Time:", os.clock() - TStart .. "s")
================================================
FILE: cli.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- cli.lua
--
-- This Script contains the Code for the Prometheus CLI
-- Configure package.path for requiring Prometheus
local function script_path()
local str = debug.getinfo(2, "S").source:sub(2)
return str:match("(.*[/%\\])") or "";
end
package.path = script_path() .. "?.lua;" .. package.path;
require("src.cli");
================================================
FILE: doc/README.md
================================================
---
description: Prometheus is an Lua Obfuscator, that is written in pure Lua.
---
# Prometheus
Prometheus can obfuscate Lua51 as well as Roblox's LuaU, which is an optionally typed superset of Lua51.
View Prometheus on [github](https://github.com/levno-710/Prometheus).
This Documentation only applies to the newest version of Prometheus.
================================================
FILE: doc/SUMMARY.md
================================================
# Table of contents
* [Prometheus](README.md)
## Getting Started
* [Installation](getting-started/installation.md)
* [Obfuscating your first script](getting-started/obfuscating-your-first-script.md)
* [Command Line Options](getting-started/command-line-options.md)
* [Presets](getting-started/presets.md)
* [Writing a custom Config File](getting-started/writing-a-custom-config-file.md)
* [The Config Object](getting-started/the-config-object.md)
## Steps
* [WrapInFunction](steps/wrapinfunction.md)
* [Vmify](steps/vmify.md)
* [SplitStrings](steps/splitstrings.md)
* [ProxifyLocals](steps/proxifylocals.md)
* [EncryptStrings](steps/encryptstrings.md)
* [ConstantArray](steps/constantarray.md)
* [AntiTamper](steps/anti-tamper.md)
## advanced
* [Using Prometheus in your Lua Application](advanced/using-prometheus-in-your-lua-application.md)
================================================
FILE: doc/advanced/using-prometheus-in-your-lua-application.md
================================================
# Using Prometheus in your Lua Application
Prometheus can also be used as a library for your custom Lua Applications instead of using its cli tool.
In order to do that you'll first need to clone the github repo:
```batch
git clone "https://github.com/levno-710/Prometheus.git"
```
After that, you'll need to copy everything within the src folder to your project. Let's say you created a folder named `prometheus`, where all the Prometheus files are located. You can the use the following code to obfuscate a string:
{% code title="use_prometheus.lua" %}
```lua
local Prometheus = require("prometheus.prometheus")
-- If you don't want console output
Prometheus.Logger.logLevel = Prometheus.Logger.LogLevel.Error
-- Your code
local code = 'print("Hello, World!")'
-- Create a Pipeline using the Strong preset
local pipeline = Prometheus.Pipeline:fromConfig(Prometheus.Presets.Strong)
-- Apply the obfuscation and print the result
print(pipeline:apply(code));
```
{% endcode %}
Instead of passing the Strong preset you could also pass a custom [Config Object](../getting-started/the-config-object.md).
================================================
FILE: doc/getting-started/command-line-options.md
================================================
# Command Line Options
The following table provides a brief overview over the command line options:
| Option | Usage |
| ----------------------------- | ----------------------------------------------------------- |
| --preset \[name]; --p \[name] | Specify the config preset to be used; [Details](presets.md) |
| --config \[path]; --c \[path] | Specify the path to a custom config file |
| --out \[path]; --o \[path] | Specify the path of the output file |
| --nocolors | Disable ansi colors escape sequences |
| --Lua51 | Handle input as Lua 5.1 |
| --LuaU | Handle input as LuaU |
| --pretty | Pretty print the output |
================================================
FILE: doc/getting-started/installation.md
================================================
# Installation
To install Prometheus, simply clone the Github Repository using:
```batch
git clone "https://github.com/levno-710/Prometheus.git"
```
Alternatively you can download the Sources [here](https://github.com/levno-710/Prometheus/archive/refs/heads/master.zip).
Prometheus also Requires LuaJIT or Lua51 in order to work. The Lua51 binaries can be downloaded [here](https://sourceforge.net/projects/luabinaries/files/5.1.5/Tools%20Executables/).
================================================
FILE: doc/getting-started/obfuscating-your-first-script.md
================================================
# Obfuscating your first script
Now that you have downloaded and Prometheus, you probably wonder how to use it. In this quick tutorial you are going to learn how to obfuscate your first file.
Note that in the following command examples `lua` should be replaced by your lua implementation.
Create the following file within the Prometheus main directory that you just downloaded:
{% code title="hello_world.lua" %}
```lua
print("Hello, World")
```
{% endcode %}
Now run the following command inside of the Prometheus directory:
```batch
lua ./cli.lua ./hello_world.lua
```
You may notice, that the console output looks weird. If that is the case, your terminal does not support ansi color escape sequences. You should add the `--nocolors` option:
```batch
lua ./cli.lua --nocolors ./hello_world.lua
```
This should create the following file:
{% code title="hello_world.obfuscated.lua" %}
```lua
print("Hello, World")
```
{% endcode %}
As you can see, the file hasn't changed at all. That is because by default prometheus is just a minifier and the code we gave it was already as small as possible. To actually obfuscate the file, prometheus must be told which obfuscation steps it should apply in which order. In order to do this, the cli provides the `--preset` option which allows you to specify the name of a predefined configuration. There are currently the following presets:
* Minify
* Weak
* Medium
* Strong
In order to perform the obfuscation, you need to specify that Prometheus should use the Strong preset:
```batch
lua ./cli.lua --preset Medium ./hello_world.lua
```
The `hello_world.obfuscated.lua` should now contain the obfuscated code that should still print "Hello World".
Note that using the "Strong" preset is not recommended for large projects.
================================================
FILE: doc/getting-started/presets.md
================================================
# Presets
The following table provides an overview over the presets
| name | size | speed |
| ------ | ------ | ------- |
| Minify | tiny | fastest |
| Weak | small | fast |
| Medium | medium | medium |
| Strong | huge | slowest |
================================================
FILE: doc/getting-started/the-config-object.md
================================================
# The Config Object
Prometheus takes a configuration object. In this object there can be many properties applied. \
The following table provides an overview:
| Property | type | possible values | default |
| ------------- | ------- | -------------------------------------------- | ----------------- |
| LuaVersion | string | "Lua51", "LuaU" | "Lua51" |
| PrettyPrint | boolean | true, false | false |
| VarNamePrefix | string | any | "" |
| NameGenerator | string | "Mangled", "MangledShuffled", "Il", "Number" | "MangledShuffled" |
| Seed | number | any | 0 |
| Steps | table | StepConfig\[] | {} |
As this table shows, all properties in the config object are optional as they have a default value.
As an example, here is the code for the minify preset:
```lua
{
-- The default LuaVersion is Lua51
LuaVersion = "Lua51";
-- For minifying no VarNamePrefix is applied
VarNamePrefix = "";
-- Name Generator for Variables
NameGenerator = "MangledShuffled";
-- No pretty printing
PrettyPrint = false;
-- Seed is generated based on current time
Seed = 0;
-- No obfuscation steps
Steps = {}
};
```
### Steps
The most important property is the Steps property. This property must be a table of so called Step Configs. A Step in Prometheus describes a single transformation applied to your script by the Prometheus obfuscation pipeline. A StepConfiguration consists of the Name of the Step as well as settings for the step. All Steps will later be applied in the order they are defined. A single Step can be defined twice and will then be applied twice.
```lua
-- Obfuscation steps
Steps = {
{
-- This obfuscation step puts all constants into an array at the beginning of the code
Name = "ConstantArray";
Settings = {
-- Apply to Strings only
StringsOnly = true;
-- Apply to all Constants, 0.5 would only affect 50% of strings
Threshold = 1;
}
},
}
```
Under [Steps](broken-reference), you can find all current Steps, their names as well as the possible options.
================================================
FILE: doc/getting-started/writing-a-custom-config-file.md
================================================
# Writing a custom Config File
Configuration Files for Prometheus are just lua modules, that return a single object, which contains the configuration. Let's say we have the following config file:
{% code title="config.lua" %}
```lua
return {
-- The default LuaVersion is Lua51
LuaVersion = "Lua51"; -- or "LuaU"
-- All Variables will start with this prefix
VarNamePrefix = "";
-- Name Generator for Variables that look like this: b, a, c, D, t, G
NameGenerator = "MangledShuffled";
-- No pretty printing
PrettyPrint = false;
-- Seed is generated based on current time
-- When specifying a seed that is not 0, you will get the same output every time
Seed = 0;
-- Obfuscation steps
Steps = {
{
-- This obfuscation step puts all constants into an array at the beginning of the code
Name = "ConstantArray";
Settings = {
-- Apply to Strings only
StringsOnly = true;
-- Apply to all Constants, 0.5 would only affect 50% of strings
Threshold = 1;
}
},
}
}
```
{% endcode %}
One can now obfuscate a script using this configuration by running:
```batch
lua ./cli.lua --config config.lua hello_world.lua
```
You should get the following output:
{% code title="hello_world.obfuscated.lua" %}
```lua
local N={"Hello, World!"}local function k(k)return N[k+40058]end print(k(-40057))
```
{% endcode %}
As you can see, the only transformation that was applied to our Hello World example was putting all strings (in this case only `"Hello, World!"` ) into an array and creating a wrapper function for retrieving the value.
### How does the Config File work?
The config file is simply a lua file, that returns the configuration object. Please note that this lua file is sandboxed by Prometheus when loading the configuration, meaning that you can't use any predefined functions like `tostring` or libraries like `math`.
See [The Config Object](the-config-object.md) to learn what this configuration object consists of.
================================================
FILE: doc/steps/anti-tamper.md
================================================
---
description: This step provides an obfuscation step, that breaks the script, when someone tries to tamper with it.
---
# Anti Tamper
### Settings
| Name | type | description | values |
| ----------- | ---- | ------------------------------------------- | --------------------------------------- |
| UseDebug | boolean | Uses the debug library in lua. Disable this if you don't have access to debug library | "true","false" |
================================================
FILE: doc/steps/constantarray.md
================================================
---
description: >-
This Step will Extract all Constants and put them into an Array at the
beginning of the script
---
# ConstantArray
### Settings
| Name | type | description |
| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| Threshold | number | The relative amount of nodes that will be affected" |
| StringsOnly | boolean | Whether to only Extract Strings |
| Shuffle | boolean | Whether to shuffle the order of Elements in the Array |
| Rotate | boolean | Whether to rotate the String Array by a specific (random) amount. This will be undone on runtime. |
| LocalWrapperThreshold | number | The relative amount of nodes functions, that will get local wrappers |
| LocalWrapperCount | number | The number of Local wrapper Functions per scope. This only applies if LocalWrapperThreshold is greater than 0 |
| LocalWrapperArgCount | number | The number of Arguments to the Local wrapper Functions |
| MaxWrapperOffset | number | The Max Offset for the Wrapper Functions |
### Example
{% code title="in.lua" %}
```lua
print("1")
print("2")
print("3")
print("4")
```
{% endcode %}
{% code title="out.lua" %}
```lua
-- LocalWrapperCount = 3
-- LocalWrapperArgCount = 5
local F = {"4", "3", "2", "1"}
do
local y, G = 1, 4
while y < G do
F[y], F[G] = F[G], F[y]
y, G = y + 1, G - 1
end
y, G = 1, 3
while y < G do
F[y], F[G] = F[G], F[y]
y, G = y + 1, G - 1
end
y, G = 4, 4
while y < G do
F[y], F[G] = F[G], F[y]
y, G = y + 1, G - 1
end
end
local function y(y)
return F[y + 440]
end
local G = {cb = function(F, G, R, p, b)
return y(G - 2277)
end, n = function(F, G, R, p, b)
return y(p + 47178)
end, B = function(F, G, R, p, b)
return y(F + 31775)
end}
print(G.cb(1575, 1840, 2367, 1293, 1280))
print(G.B(-32213, -31781, -31538, -32780, -32728))
print(G.B(-32214, -33004, -31973, -32125, -31855))
print(G.B(-32211, -31884, -31217, -32222, -31210))
```
{% endcode %}
================================================
FILE: doc/steps/encryptstrings.md
================================================
---
description: This Step will encrypt all String constants in your code
---
# EncryptStrings
## Settings
None
## Example
{% code title="in.lua" %}
```lua
print("Hello, World!")
```
{% endcode %}
{% code title="out.lua" %}
```lua
-- Settings: None
local x, F
do
local k = math.floor
local I = math.random
local Y = table.remove
local i = string.char
local K = 0
local J = 2
local Q = {}
local W = {}
local q = 0
local R = {}
for F = 1, 256, 1 do
R[F] = F
end
repeat
local F = I(1, #R)
local x = Y(R, F)
W[x] = i(x - 1)
until #R == 0
local j = {}
local function B()
if #j == 0 then
K = (K * 173 + 8408159861491) % 35184372088832
repeat
J = (J * 160) % 257
until J ~= 1
local F = J % 32
local x = (k(K / 2 ^ (13 - (J - F) / 32)) % 4294967296) / 2 ^ F
local I = k((x % 1) * 4294967296) + k(x)
local Y = I % 65536
local i = (I - Y) / 65536
local Q = Y % 256
local W = (Y - Q) / 256
local q = i % 256
local R = (i - q) / 256
j = {Q, W, q, R}
end
return table.remove(j)
end
local d = {}
x = setmetatable({}, {__index = d, __metatable = nil})
function F(x, k)
local I = d
if I[k] then
else
j = {}
local F = W
K = k % 35184372088832
J = k % 255 + 2
local Y = string.len(x)
I[k] = ""
local i = 198
for Y = 1, Y, 1 do
i = ((string.byte(x, Y) + B()) + i) % 256
I[k] = I[k] .. F[i + 1]
end
end
return k
end
end
print(x[F("\219\018Q%~Y\225\128u\128\208&\155", 6909832146399)])
```
{% endcode %}
================================================
FILE: doc/steps/proxifylocals.md
================================================
---
description: This Step wraps all locals into Proxy Objects
---
# ProxifyLocals
### Settings
| Name | type | description | values |
| ----------- | ---- | ------------------------------------------- | --------------------------------------- |
| LiteralType | enum | The type of the randomly generated literals | "dictionary", "number", "string", "any" |
### Example
{% code title="in.lua" %}
```lua
local x = "Hello, World!"
print(x)
```
{% endcode %}
{% code title="out.lua" %}
```lua
-- LiteralType = "dictionary"
local n = setmetatable
local D =
n(
{Wz = function()
end},
{__div = function(R, n)
R.Wz = n
end, __concat = function(R, n)
return R.Wz
end}
)
local R =
n(
{Js = "Hello, World!"},
{__add = function(R, n)
R.Js = n
end, __index = function(R, n)
return rawget(R, "Js")
end}
)
print(R.Muirgen)
```
{% endcode %}
================================================
FILE: doc/steps/splitstrings.md
================================================
---
description: This Step splits Strings to a specific or random length
---
# SplitStrings
### Settings
| Name | type | description | Values |
| ------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| Threshold | number | The relative amount of nodes that will be affected | 0 <= x <= 1 |
| MinLength | number | The minimal length for the chunks in that the Strings are splitted | x > 0 |
| MaxLength | number | The maximal length for the chunks in that the Strings are splitted | x >= MinLength |
| ConcatenationType | enum | The Functions used for Concatenation. Note that when using custom, the String Array will also be Shuffled | "strcat", "table", "custom" |
| CustomFunctionType | enum |
The Type of Function code injection This Option only applies when custom Concatenation is selected.
Note that when choosing inline, the code size may increase significantly!
| "global", "local", "inline" |
| CustomLocalFunctionsCount | number | The number of local functions per scope. This option only applies when CustomFunctionType = local | x > 0 |
### Example
{% code title="in.lua" %}
```lua
print("Hello, World!")
```
{% endcode %}
{% code title="out.lua" %}
```lua
-- MinLength = 1
-- MaxLength = 1
local f = function(f)
local k, C = f[#f], ""
for j = 1, #k, 1 do
C = C .. k[f[j]]
end
return C
end
print(f({13, 11, 4, 12, 1, 6, 8, 10, 9, 7, 3, 2, 5, {"o", "d", "l", "l", "!", ",", "r", " ", "o", "W", "e", "l", "H"}}))
```
{% endcode %}
================================================
FILE: doc/steps/vmify.md
================================================
---
description: This Step will Compile your script and run it within a VM.
---
# Vmify
### Settings
None
================================================
FILE: doc/steps/wrapinfunction.md
================================================
---
description: This Step Wraps the Entire Script into a Function
---
# WrapInFunction
### Settings
| Name | type | description |
| ---------- | ------ | ------------------------ |
| Iterations | number | The Number Of Iterations |
### Example
{% code title="in.lua" %}
```lua
print("Hello, World!")
```
{% endcode %}
{% code title="out.lua" %}
```lua
-- Iterations = 1
return (function()
print("Hello, World!")
end)()
```
{% endcode %}
================================================
FILE: prometheus-main.lua
================================================
require("cli")
================================================
FILE: readme.md
================================================
# :fire: Prometheus Lua Obfuscator
[](https://github.com/prometheus-lua/Prometheus/actions/workflows/Test.yml)
Prometheus is a Lua obfuscator written in pure Lua.
It uses several AST-based transformations including Control-Flow Flattening, Constant Encryption and more.
This project was inspired by the amazing [javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator).
It can currently obfuscate Lua51 and Roblox's LuaU, however LuaU support is not finished yet.
You can find the full Documentation including a getting started guide [here](https://levno-710.gitbook.io/prometheus/).
Prometheus has an official [Discord server](https://discord.gg/U8h4d4Rf64).
## Installation
To install Prometheus, simply clone the GitHub repository using:
```batch
git clone https://github.com/prometheus-lua/Prometheus.git
```
Alternatively you can download the sources [here](https://github.com/prometheus-lua/Prometheus/archive/refs/heads/master.zip).
Prometheus also Requires LuaJIT or Lua51 in order to work. The Lua51 binaries can be downloaded [here](https://sourceforge.net/projects/luabinaries/files/5.1.5/Tools%20Executables/).
## Usage
To quickly obfuscate a script:
```batch
lua ./cli.lua --preset Medium ./your_file.lua
```
### Example output
```lua
-- input.lua
print("Hello, World!");
```
```lua
-- input.obfuscated.lua
return(function(...)local L={"afT6mf1V","/7mJXsuvmE1c/fT3";"tn1ZSn6=","37ghSJM=";"WqermfWAWuuZpb3XX7M=","tqXGSJ3u","XQXpL9x21dxAWJa//p==","SrM=";"3q+5SJM=","/D==";"t7XUt0p=";"mIeOmIx9";"LdgrBfWdWuNABsb+KJxj","SJWJ4dahKsebW7t+KQv=","/cDu3AvP/D==";"Llv7uD==","tJWhFfTE";"TQ43ctIuy9HIop==","mEu93p==";"WJax1sXEXEaxWuxGt6==","t0gPSEp=",...
-- remaining obfuscated output omitted
```
For more advanced use cases see the [Documentation](https://levno-710.gitbook.io/prometheus/).
## Tests
To perform the Prometheus Tests, just run
```batch
lua ./tests.lua [--Linux]
```
## License and Commercial Use
Prometheus is licensed under the Prometheus License, a modified MIT-style license.
You are free to use, modify, and distribute this software, including for commercial purposes, under the following conditions:
- Any commercial product, wrapper, or service (including SaaS or hosted solutions) that uses or integrates Prometheus must include clear attribution to:
```
Based on Prometheus by Elias Oelschner, https://github.com/prometheus-lua/Prometheus
```
- The attribution must be visible in the product’s UI, documentation, and public website.
- The obfuscated output files generated by Prometheus do not need to include any license or copyright notice.
- Derivative works and public forks must also include a statement in their README noting that they are based on Prometheus.
Full license text: [Prometheus License](https://github.com/levno-710/Prometheus/blob/master/LICENSE)
================================================
FILE: src/cli.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- cli.lua
--
-- This Script contains the Code for the Prometheus CLI.
-- Configure package.path for requiring Prometheus.
local function script_path()
local str = debug.getinfo(2, "S").source:sub(2)
return str:match("(.*[/%\\])")
end
package.path = script_path() .. "?.lua;" .. package.path
---@diagnostic disable-next-line: different-requires
local Prometheus = require("prometheus")
Prometheus.Logger.logLevel = Prometheus.Logger.LogLevel.Info
-- Check if the file exists
local function file_exists(file)
local f = io.open(file, "rb")
if f then
f:close()
end
return f ~= nil
end
string.split = function(str, sep)
local fields = {}
local pattern = string.format("([^%s]+)", sep)
str:gsub(pattern, function(c)
fields[#fields + 1] = c
end)
return fields
end
-- get all lines from a file, returns an empty
-- list/table if the file does not exist
local function lines_from(file)
if not file_exists(file) then
return {}
end
local lines = {}
for line in io.lines(file) do
lines[#lines + 1] = line
end
return lines
end
local function load_chunk(content, chunkName, environment)
if type(loadstring) == "function" then
local func, err = loadstring(content, chunkName)
if not func then
return nil, err
end
if environment and type(setfenv) == "function" then
setfenv(func, environment)
elseif environment and type(load) == "function" then
return load(content, chunkName, "t", environment)
end
return func
end
if type(load) ~= "function" then
return nil, "No load function available"
end
return load(content, chunkName, "t", environment)
end
-- CLI
local config, sourceFile, outFile, luaVersion, prettyPrint
Prometheus.colors.enabled = true
-- Parse Arguments
local i = 1
while i <= #arg do
local curr = arg[i]
if curr:sub(1, 2) == "--" then
if curr == "--preset" or curr == "--p" then
if config then
Prometheus.Logger:warn("The config was set multiple times")
end
i = i + 1
local preset = Prometheus.Presets[arg[i]]
if not preset then
Prometheus.Logger:error(string.format('A Preset with the name "%s" was not found!', tostring(arg[i])))
end
config = preset
elseif curr == "--config" or curr == "--c" then
i = i + 1
local filename = tostring(arg[i])
if not file_exists(filename) then
Prometheus.Logger:error(string.format('The config file "%s" was not found!', filename))
end
local content = table.concat(lines_from(filename), "\n")
-- Load Config from File
local func, err = load_chunk(content, "@" .. filename, {})
if not func then
Prometheus.Logger:error(string.format('Failed to parse config file "%s": %s', filename, tostring(err)))
end
config = func()
elseif curr == "--out" or curr == "--o" then
i = i + 1
if outFile then
Prometheus.Logger:warn("The output file was specified multiple times!")
end
outFile = arg[i]
elseif curr == "--nocolors" then
Prometheus.colors.enabled = false
elseif curr == "--Lua51" then
luaVersion = "Lua51"
elseif curr == "--LuaU" then
luaVersion = "LuaU"
elseif curr == "--pretty" then
prettyPrint = true
elseif curr == "--saveerrors" then
-- Override error callback
Prometheus.Logger.errorCallback = function(...)
print(Prometheus.colors(Prometheus.Config.NameUpper .. ": " .. ..., "red"))
local args = { ... }
local message = table.concat(args, " ")
local fileName = sourceFile:sub(-4) == ".lua" and sourceFile:sub(0, -5) .. ".error.txt"
or sourceFile .. ".error.txt"
local handle = io.open(fileName, "w")
handle:write(message)
handle:close()
os.exit(1)
end
else
Prometheus.Logger:warn(string.format('The option "%s" is not valid and therefore ignored', curr))
end
else
if sourceFile then
Prometheus.Logger:error(string.format('Unexpected argument "%s"', arg[i]))
end
sourceFile = tostring(arg[i])
end
i = i + 1
end
if not sourceFile then
Prometheus.Logger:error("No input file was specified!")
end
if not config then
Prometheus.Logger:warn("No config was specified, falling back to Minify preset")
config = Prometheus.Presets.Minify
end
-- Add Option to override Lua Version
config.LuaVersion = luaVersion or config.LuaVersion
config.PrettyPrint = prettyPrint ~= nil and prettyPrint or config.PrettyPrint
if not file_exists(sourceFile) then
Prometheus.Logger:error(string.format('The File "%s" was not found!', sourceFile))
end
if not outFile then
if sourceFile:sub(-4) == ".lua" then
outFile = sourceFile:sub(0, -5) .. ".obfuscated.lua"
else
outFile = sourceFile .. ".obfuscated.lua"
end
end
local source = table.concat(lines_from(sourceFile), "\n")
local pipeline = Prometheus.Pipeline:fromConfig(config)
local out = pipeline:apply(source, sourceFile)
Prometheus.Logger:info(string.format('Writing output to "%s"', outFile))
-- Write Output
local handle = io.open(outFile, "w")
handle:write(out)
handle:close()
================================================
FILE: src/colors.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- colors.lua
--
-- This Script provides a simple method for syntax highlighting of Lua code
local keys = {
reset = 0,
bright = 1,
dim = 2,
underline = 4,
blink = 5,
reverse = 7,
hidden = 8,
black = 30,
pink = 91,
red = 31,
green = 32,
yellow = 33,
blue = 34,
magenta = 35,
cyan = 36,
grey = 37,
gray = 37,
white = 97,
blackbg = 40,
redbg = 41,
greenbg = 42,
yellowbg = 43,
bluebg = 44,
magentabg = 45,
cyanbg = 46,
greybg = 47,
graybg = 47,
whitebg = 107,
}
local escapeString = string.char(27) .. "[%dm"
local function escapeNumber(number)
return escapeString:format(number)
end
local settings = {
enabled = true,
}
local function colors(str, ...)
if not settings.enabled then
return str
end
str = tostring(str or "")
local escapes = {}
for _, name in ipairs({ ... }) do
table.insert(escapes, escapeNumber(keys[name]))
end
return escapeNumber(keys.reset) .. table.concat(escapes) .. str .. escapeNumber(keys.reset)
end
return setmetatable(settings, {
__call = function(_, ...)
return colors(...)
end,
})
================================================
FILE: src/highlightlua.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- highlightlua.lua
--
-- This Script provides a simple Method for Syntax Highlighting of Lua code
local Tokenizer = require("prometheus.tokenizer");
local colors = require("colors");
local TokenKind = Tokenizer.TokenKind;
local lookupify = require("prometheus.util").lookupify;
return function(code, luaVersion)
local out = "";
local tokenizer = Tokenizer:new({
LuaVersion = luaVersion,
});
tokenizer:append(code);
local tokens = tokenizer:scanAll();
local nonColorSymbols = lookupify{
",", ";", "(", ")", "{", "}", ".", ":", "[", "]"
}
local defaultGlobals = lookupify{
"string", "table", "bit32", "bit"
}
local currentPos = 1;
for _, token in ipairs(tokens) do
if token.startPos >= currentPos then
out = out .. string.sub(code, currentPos, token.startPos);
end
if token.kind == TokenKind.Ident then
if defaultGlobals[token.source] then
out = out .. colors(token.source, "red");
else
out = out .. token.source;
end
elseif token.kind == TokenKind.Keyword then
if token.source == "nil" then
out = out .. colors(token.source, "yellow");
else
out = out .. colors(token.source, "yellow");
end
elseif token.kind == TokenKind.Symbol then
if nonColorSymbols[token.source] then
out = out .. token.source;
else
out = out .. colors(token.source, "yellow");
end
elseif token.kind == TokenKind.String then
out = out .. colors(token.source, "green")
elseif token.kind == TokenKind.Number then
out = out .. colors(token.source, "red")
else
out = out .. token.source;
end
currentPos = token.endPos + 1;
end
return out;
end
================================================
FILE: src/logger.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- logger.lua
--
-- This Script provides a Logger for Prometheus.
local logger = {}
local config = require("config");
local colors = require("colors");
logger.LogLevel = {
Error = 0,
Warn = 1,
Log = 2,
Info = 2,
Debug = 3,
}
logger.logLevel = logger.LogLevel.Log;
logger.debugCallback = function(...)
print(colors(config.NameUpper .. ": " .. ..., "grey"));
end;
function logger:debug(...)
if self.logLevel >= self.LogLevel.Debug then
self.debugCallback(...);
end
end
logger.logCallback = function(...)
print(colors(config.NameUpper .. ": ", "magenta") .. ...);
end;
function logger:log(...)
if self.logLevel >= self.LogLevel.Log then
self.logCallback(...);
end
end
function logger:info(...)
if self.logLevel >= self.LogLevel.Log then
self.logCallback(...);
end
end
logger.warnCallback = function(...)
print(colors(config.NameUpper .. ": " .. ..., "yellow"));
end;
function logger:warn(...)
if self.logLevel >= self.LogLevel.Warn then
self.warnCallback(...);
end
end
logger.errorCallback = function(...)
print(colors(config.NameUpper .. ": " .. ..., "red"))
error(...);
end;
function logger:error(...)
self.errorCallback(...);
error(config.NameUpper .. ": logger.errorCallback did not throw an Error!");
end
return logger;
================================================
FILE: src/presets.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- presets.lua
--
-- This Script provides the predefined obfuscation presets for Prometheus
return {
-- Minifies your code. Does not obfuscate it. No performance loss.
["Minify"] = {
LuaVersion = "Lua51",
VarNamePrefix = "",
NameGenerator = "MangledShuffled",
PrettyPrint = false,
Seed = 0,
Steps = {},
},
-- Weak obfuscation. Very readable, low performance loss.
["Weak"] = {
LuaVersion = "Lua51",
VarNamePrefix = "",
NameGenerator = "MangledShuffled",
PrettyPrint = false,
Seed = 0,
Steps = {
{ Name = "Vmify", Settings = {} },
{
Name = "ConstantArray",
Settings = {
Threshold = 1,
StringsOnly = true
},
},
{ Name = "WrapInFunction", Settings = {} },
},
},
-- This is here for the tests.lua file.
-- It helps isolate any problems with the Vmify step.
-- It is not recommended to use this preset for obfuscation.
-- Use the Weak, Medium, or Strong for obfuscation instead.
["Vmify"] = {
LuaVersion = "Lua51",
VarNamePrefix = "",
NameGenerator = "MangledShuffled",
PrettyPrint = false,
Seed = 0,
Steps = {
{ Name = "Vmify", Settings = {} },
},
},
-- Medium obfuscation. Moderate obfuscation, moderate performance loss.
["Medium"] = {
LuaVersion = "Lua51",
VarNamePrefix = "",
NameGenerator = "MangledShuffled",
PrettyPrint = false,
Seed = 0,
Steps = {
{ Name = "EncryptStrings", Settings = {} },
{
Name = "AntiTamper",
Settings = {
UseDebug = false,
},
},
{ Name = "Vmify", Settings = {} },
{
Name = "ConstantArray",
Settings = {
Threshold = 1,
StringsOnly = true,
Shuffle = true,
Rotate = true,
LocalWrapperThreshold = 0,
},
},
{ Name = "NumbersToExpressions", Settings = {} },
{ Name = "WrapInFunction", Settings = {} },
},
},
-- Strong obfuscation, high performance loss.
["Strong"] = {
LuaVersion = "Lua51",
VarNamePrefix = "",
NameGenerator = "MangledShuffled",
PrettyPrint = false,
Seed = 0,
Steps = {
{ Name = "Vmify", Settings = {} },
{ Name = "EncryptStrings", Settings = {} },
{
Name = "AntiTamper",
Settings = {
UseDebug = false,
},
},
{ Name = "Vmify", Settings = {} },
{
Name = "ConstantArray",
Settings = {
Threshold = 1,
StringsOnly = true,
Shuffle = true,
Rotate = true,
LocalWrapperThreshold = 0
},
},
{
Name = "NumbersToExpressions",
Settings = {
NumberRepresentationMutaton = true
},
},
{ Name = "WrapInFunction", Settings = {} },
},
},
}
================================================
FILE: src/prometheus/ast.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- ast.lua
--
-- This Script provides the Abstract Syntax Tree (AST) for Prometheus.
local Ast = {}
local AstKind = {
-- Misc
TopNode = "TopNode";
Block = "Block";
-- Statements
ContinueStatement = "ContinueStatement";
BreakStatement = "BreakStatement";
DoStatement = "DoStatement";
WhileStatement = "WhileStatement";
ReturnStatement = "ReturnStatement";
RepeatStatement = "RepeatStatement";
ForInStatement = "ForInStatement";
ForStatement = "ForStatement";
IfStatement = "IfStatement";
FunctionDeclaration = "FunctionDeclaration";
LocalFunctionDeclaration = "LocalFunctionDeclaration";
LocalVariableDeclaration = "LocalVariableDeclaration";
FunctionCallStatement = "FunctionCallStatement";
PassSelfFunctionCallStatement = "PassSelfFunctionCallStatement";
AssignmentStatement = "AssignmentStatement";
-- LuaU Compound Statements
CompoundAddStatement = "CompoundAddStatement";
CompoundSubStatement = "CompoundSubStatement";
CompoundMulStatement = "CompoundMulStatement";
CompoundDivStatement = "CompoundDivStatement";
CompoundModStatement = "CompoundModStatement";
CompoundPowStatement = "CompoundPowStatement";
CompoundConcatStatement = "CompoundConcatStatement";
-- Assignment Index
AssignmentIndexing = "AssignmentIndexing";
AssignmentVariable = "AssignmentVariable";
-- Expression Nodes
BooleanExpression = "BooleanExpression";
NumberExpression = "NumberExpression";
StringExpression = "StringExpression";
NilExpression = "NilExpression";
VarargExpression = "VarargExpression";
OrExpression = "OrExpression";
AndExpression = "AndExpression";
LessThanExpression = "LessThanExpression";
GreaterThanExpression = "GreaterThanExpression";
LessThanOrEqualsExpression = "LessThanOrEqualsExpression";
GreaterThanOrEqualsExpression = "GreaterThanOrEqualsExpression";
NotEqualsExpression = "NotEqualsExpression";
EqualsExpression = "EqualsExpression";
StrCatExpression = "StrCatExpression";
AddExpression = "AddExpression";
SubExpression = "SubExpression";
MulExpression = "MulExpression";
DivExpression = "DivExpression";
ModExpression = "ModExpression";
NotExpression = "NotExpression";
LenExpression = "LenExpression";
NegateExpression = "NegateExpression";
PowExpression = "PowExpression";
IndexExpression = "IndexExpression";
FunctionCallExpression = "FunctionCallExpression";
PassSelfFunctionCallExpression = "PassSelfFunctionCallExpression";
VariableExpression = "VariableExpression";
FunctionLiteralExpression = "FunctionLiteralExpression";
TableConstructorExpression = "TableConstructorExpression";
-- Table Entry
TableEntry = "TableEntry";
KeyedTableEntry = "KeyedTableEntry";
-- Misc
NopStatement = "NopStatement";
IfElseExpression = "IfElseExpression";
}
local astKindExpressionLookup = {
[AstKind.BooleanExpression] = 0;
[AstKind.NumberExpression] = 0;
[AstKind.StringExpression] = 0;
[AstKind.NilExpression] = 0;
[AstKind.VarargExpression] = 0;
[AstKind.OrExpression] = 12;
[AstKind.AndExpression] = 11;
[AstKind.LessThanExpression] = 10;
[AstKind.GreaterThanExpression] = 10;
[AstKind.LessThanOrEqualsExpression] = 10;
[AstKind.GreaterThanOrEqualsExpression] = 10;
[AstKind.NotEqualsExpression] = 10;
[AstKind.EqualsExpression] = 10;
[AstKind.StrCatExpression] = 9;
[AstKind.AddExpression] = 8;
[AstKind.SubExpression] = 8;
[AstKind.MulExpression] = 7;
[AstKind.DivExpression] = 7;
[AstKind.ModExpression] = 7;
[AstKind.NotExpression] = 5;
[AstKind.LenExpression] = 5;
[AstKind.NegateExpression] = 5;
[AstKind.PowExpression] = 4;
[AstKind.IndexExpression] = 1;
[AstKind.AssignmentIndexing] = 1;
[AstKind.FunctionCallExpression] = 2;
[AstKind.PassSelfFunctionCallExpression] = 2;
[AstKind.VariableExpression] = 0;
[AstKind.AssignmentVariable] = 0;
[AstKind.FunctionLiteralExpression] = 3;
[AstKind.TableConstructorExpression] = 3;
}
Ast.AstKind = AstKind;
function Ast.astKindExpressionToNumber(kind)
return astKindExpressionLookup[kind] or 100;
end
function Ast.ConstantNode(val)
if type(val) == "nil" then
return Ast.NilExpression();
end
if type(val) == "string" then
return Ast.StringExpression(val);
end
if type(val) == "number" then
return Ast.NumberExpression(val);
end
if type(val) == "boolean" then
return Ast.BooleanExpression(val);
end
end
function Ast.NopStatement()
return {
kind = AstKind.NopStatement;
}
end
function Ast.IfElseExpression(condition, true_value, false_value)
return {
kind = AstKind.IfElseExpression,
condition = condition,
true_value = true_value,
false_value = false_value
}
end
-- Create Ast Top Node
function Ast.TopNode(body, globalScope)
return {
kind = AstKind.TopNode,
body = body,
globalScope = globalScope,
}
end
function Ast.TableEntry(value)
return {
kind = AstKind.TableEntry,
value = value,
}
end
function Ast.KeyedTableEntry(key, value)
return {
kind = AstKind.KeyedTableEntry,
key = key,
value = value,
}
end
function Ast.TableConstructorExpression(entries)
return {
kind = AstKind.TableConstructorExpression,
entries = entries,
};
end
-- Create Statement Block
function Ast.Block(statements, scope)
return {
kind = AstKind.Block,
statements = statements,
scope = scope,
}
end
-- Create Break Statement
function Ast.BreakStatement(loop, scope)
return {
kind = AstKind.BreakStatement,
loop = loop,
scope = scope,
}
end
-- Create Continue Statement
function Ast.ContinueStatement(loop, scope)
return {
kind = AstKind.ContinueStatement,
loop = loop,
scope = scope,
}
end
function Ast.PassSelfFunctionCallStatement(base, passSelfFunctionName, args)
return {
kind = AstKind.PassSelfFunctionCallStatement,
base = base,
passSelfFunctionName = passSelfFunctionName,
args = args,
}
end
function Ast.AssignmentStatement(lhs, rhs)
if(#lhs < 1) then
print(debug.traceback());
error("Something went wrong!");
end
return {
kind = AstKind.AssignmentStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.CompoundAddStatement(lhs, rhs)
return {
kind = AstKind.CompoundAddStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.CompoundSubStatement(lhs, rhs)
return {
kind = AstKind.CompoundSubStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.CompoundMulStatement(lhs, rhs)
return {
kind = AstKind.CompoundMulStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.CompoundDivStatement(lhs, rhs)
return {
kind = AstKind.CompoundDivStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.CompoundPowStatement(lhs, rhs)
return {
kind = AstKind.CompoundPowStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.CompoundModStatement(lhs, rhs)
return {
kind = AstKind.CompoundModStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.CompoundConcatStatement(lhs, rhs)
return {
kind = AstKind.CompoundConcatStatement,
lhs = lhs,
rhs = rhs,
}
end
function Ast.FunctionCallStatement(base, args)
return {
kind = AstKind.FunctionCallStatement,
base = base,
args = args,
}
end
function Ast.ReturnStatement(args)
return {
kind = AstKind.ReturnStatement,
args = args,
}
end
function Ast.DoStatement(body)
return {
kind = AstKind.DoStatement,
body = body,
}
end
function Ast.WhileStatement(body, condition, parentScope)
return {
kind = AstKind.WhileStatement,
body = body,
condition = condition,
parentScope = parentScope,
}
end
function Ast.ForInStatement(scope, vars, expressions, body, parentScope)
return {
kind = AstKind.ForInStatement,
scope = scope,
ids = vars,
vars = vars,
expressions = expressions,
body = body,
parentScope = parentScope,
}
end
function Ast.ForStatement(scope, id, initialValue, finalValue, incrementBy, body, parentScope)
return {
kind = AstKind.ForStatement,
scope = scope,
id = id,
initialValue = initialValue,
finalValue = finalValue,
incrementBy = incrementBy,
body = body,
parentScope = parentScope,
}
end
function Ast.RepeatStatement(condition, body, parentScope)
return {
kind = AstKind.RepeatStatement,
body = body,
condition = condition,
parentScope = parentScope,
}
end
function Ast.IfStatement(condition, body, elseifs, elsebody)
return {
kind = AstKind.IfStatement,
condition = condition,
body = body,
elseifs = elseifs,
elsebody = elsebody,
}
end
function Ast.FunctionDeclaration(scope, id, indices, args, body)
return {
kind = AstKind.FunctionDeclaration,
scope = scope,
baseScope = scope,
id = id,
baseId = id,
indices = indices,
args = args,
body = body,
getName = function(self)
return self.scope:getVariableName(self.id);
end,
}
end
function Ast.LocalFunctionDeclaration(scope, id, args, body)
return {
kind = AstKind.LocalFunctionDeclaration,
scope = scope,
id = id,
args = args,
body = body,
getName = function(self)
return self.scope:getVariableName(self.id);
end,
}
end
function Ast.LocalVariableDeclaration(scope, ids, expressions)
return {
kind = AstKind.LocalVariableDeclaration,
scope = scope,
ids = ids,
expressions = expressions,
}
end
function Ast.VarargExpression()
return {
kind = AstKind.VarargExpression;
isConstant = false,
}
end
function Ast.BooleanExpression(value)
return {
kind = AstKind.BooleanExpression,
isConstant = true,
value = value,
}
end
function Ast.NilExpression()
return {
kind = AstKind.NilExpression,
isConstant = true,
value = nil,
}
end
function Ast.NumberExpression(value)
return {
kind = AstKind.NumberExpression,
isConstant = true,
value = value,
}
end
function Ast.StringExpression(value)
return {
kind = AstKind.StringExpression,
isConstant = true,
value = value,
}
end
function Ast.OrExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value or rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.OrExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.AndExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value and rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.AndExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.LessThanExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value < rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.LessThanExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.GreaterThanExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value > rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.GreaterThanExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.LessThanOrEqualsExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value <= rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.LessThanOrEqualsExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.GreaterThanOrEqualsExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value >= rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.GreaterThanOrEqualsExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.NotEqualsExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value ~= rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.NotEqualsExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.EqualsExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value == rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.EqualsExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.StrCatExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value .. rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.StrCatExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.AddExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value + rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.AddExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.SubExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value - rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.SubExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.MulExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value * rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.MulExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.DivExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant and rhs.value ~= 0) then
local success, val = pcall(function() return lhs.value / rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.DivExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.ModExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value % rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.ModExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.NotExpression(rhs, simplify)
if(simplify and rhs.isConstant) then
local success, val = pcall(function() return not rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.NotExpression,
rhs = rhs,
isConstant = false,
}
end
function Ast.NegateExpression(rhs, simplify)
if(simplify and rhs.isConstant) then
local success, val = pcall(function() return -rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.NegateExpression,
rhs = rhs,
isConstant = false,
}
end
function Ast.LenExpression(rhs, simplify)
if(simplify and rhs.isConstant) then
local success, val = pcall(function() return #rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.LenExpression,
rhs = rhs,
isConstant = false,
}
end
function Ast.PowExpression(lhs, rhs, simplify)
if(simplify and rhs.isConstant and lhs.isConstant) then
local success, val = pcall(function() return lhs.value ^ rhs.value end);
if success then
return Ast.ConstantNode(val);
end
end
return {
kind = AstKind.PowExpression,
lhs = lhs,
rhs = rhs,
isConstant = false,
}
end
function Ast.IndexExpression(base, index)
return {
kind = AstKind.IndexExpression,
base = base,
index = index,
isConstant = false,
}
end
function Ast.AssignmentIndexing(base, index)
return {
kind = AstKind.AssignmentIndexing,
base = base,
index = index,
isConstant = false,
}
end
function Ast.PassSelfFunctionCallExpression(base, passSelfFunctionName, args)
return {
kind = AstKind.PassSelfFunctionCallExpression,
base = base,
passSelfFunctionName = passSelfFunctionName,
args = args,
}
end
function Ast.FunctionCallExpression(base, args)
return {
kind = AstKind.FunctionCallExpression,
base = base,
args = args,
}
end
function Ast.VariableExpression(scope, id)
scope:addReference(id);
return {
kind = AstKind.VariableExpression,
scope = scope,
id = id,
getName = function(self)
return self.scope.getVariableName(self.id);
end,
}
end
function Ast.AssignmentVariable(scope, id)
scope:addReference(id);
return {
kind = AstKind.AssignmentVariable,
scope = scope,
id = id,
getName = function(self)
return self.scope.getVariableName(self.id);
end,
}
end
function Ast.FunctionLiteralExpression(args, body)
return {
kind = AstKind.FunctionLiteralExpression,
args = args,
body = body,
}
end
return Ast;
================================================
FILE: src/prometheus/compiler/block.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- block.lua
--
-- Block management for the compiler
local Scope = require("prometheus.scope");
local util = require("prometheus.util");
local lookupify = util.lookupify;
return function(Compiler)
function Compiler:createBlock()
local id;
repeat
id = math.random(0, 2^24)
until not self.usedBlockIds[id];
self.usedBlockIds[id] = true;
local scope = Scope:new(self.containerFuncScope);
local block = {
id = id;
statements = {};
scope = scope;
advanceToNextBlock = true;
};
table.insert(self.blocks, block);
return block;
end
function Compiler:setActiveBlock(block)
self.activeBlock = block;
end
function Compiler:addStatement(statement, writes, reads, usesUpvals)
if(self.activeBlock.advanceToNextBlock) then
table.insert(self.activeBlock.statements, {
statement = statement,
writes = lookupify(writes),
reads = lookupify(reads),
usesUpvals = usesUpvals or false,
});
end
end
end
================================================
FILE: src/prometheus/compiler/compile_core.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- compile_core.lua
-- This Script contains the core compilation functions: compileTopNode, compileFunction, compileBlock,
-- compileStatement, and compileExpression
local compileTop = require("prometheus.compiler.compile_top");
local statementHandlers = require("prometheus.compiler.statements");
local expressionHandlers = require("prometheus.compiler.expressions");
local Ast = require("prometheus.ast");
local logger = require("logger");
return function(Compiler)
compileTop(Compiler);
function Compiler:compileStatement(statement, funcDepth)
local handler = statementHandlers[statement.kind];
if handler then
handler(self, statement, funcDepth);
return;
end
logger:error(string.format("%s is not a compileable statement!", statement.kind));
end
function Compiler:compileExpression(expression, funcDepth, numReturns)
local handler = expressionHandlers[expression.kind];
if handler then
return handler(self, expression, funcDepth, numReturns);
end
logger:error(string.format("%s is not an compliable expression!", expression.kind));
end
end
================================================
FILE: src/prometheus/compiler/compile_top.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- compile_top.lua
--
-- This Script contains the compilation of the top node, function, and block.
local Ast = require("prometheus.ast");
local util = require("prometheus.util");
local visitast = require("prometheus.visitast");
local lookupify = util.lookupify;
local AstKind = Ast.AstKind;
return function(Compiler)
function Compiler:compileTopNode(node)
local startBlock = self:createBlock();
local scope = startBlock.scope;
self.startBlockId = startBlock.id;
self:setActiveBlock(startBlock);
local varAccessLookup = lookupify{
AstKind.AssignmentVariable,
AstKind.VariableExpression,
AstKind.FunctionDeclaration,
AstKind.LocalFunctionDeclaration,
}
local functionLookup = lookupify{
AstKind.FunctionDeclaration,
AstKind.LocalFunctionDeclaration,
AstKind.FunctionLiteralExpression,
AstKind.TopNode,
}
visitast(node, function(node, data)
if node.kind == AstKind.Block then
node.scope.__depth = data.functionData.depth;
end
if varAccessLookup[node.kind] then
if not node.scope.isGlobal then
if node.scope.__depth < data.functionData.depth then
if not self:isUpvalue(node.scope, node.id) then
self:makeUpvalue(node.scope, node.id);
end
end
end
end
end, nil, nil)
self.varargReg = self:allocRegister(true);
scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);
scope:addReferenceToHigherScope(self.scope, self.selectVar);
scope:addReferenceToHigherScope(self.scope, self.unpackVar);
self:addStatement(self:setRegister(scope, self.varargReg, Ast.VariableExpression(self.containerFuncScope, self.argsVar)), {self.varargReg}, {}, false);
self:compileBlock(node.body, 0);
if(self.activeBlock.advanceToNextBlock) then
self:addStatement(self:setPos(self.activeBlock.scope, nil), {self.POS_REGISTER}, {}, false);
self:addStatement(self:setReturn(self.activeBlock.scope, Ast.TableConstructorExpression({})), {self.RETURN_REGISTER}, {}, false)
self.activeBlock.advanceToNextBlock = false;
end
self:resetRegisters();
end
function Compiler:compileFunction(node, funcDepth)
funcDepth = funcDepth + 1;
local oldActiveBlock = self.activeBlock;
local upperVarargReg = self.varargReg;
self.varargReg = nil;
local upvalueExpressions = {};
local upvalueIds = {};
local usedRegs = {};
local oldGetUpvalueId = self.getUpvalueId;
self.getUpvalueId = function(self, scope, id)
if(not upvalueIds[scope]) then
upvalueIds[scope] = {};
end
if(upvalueIds[scope][id]) then
return upvalueIds[scope][id];
end
local scopeFuncDepth = self.scopeFunctionDepths[scope];
local expression;
if(scopeFuncDepth == funcDepth) then
oldActiveBlock.scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);
expression = Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {});
elseif(scopeFuncDepth == funcDepth - 1) then
local varReg = self:getVarRegister(scope, id, scopeFuncDepth, nil);
expression = self:register(oldActiveBlock.scope, varReg);
table.insert(usedRegs, varReg);
else
local higherId = oldGetUpvalueId(self, scope, id);
oldActiveBlock.scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);
expression = Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(higherId));
end
table.insert(upvalueExpressions, Ast.TableEntry(expression));
local uid = #upvalueExpressions;
upvalueIds[scope][id] = uid;
return uid;
end
local block = self:createBlock();
self:setActiveBlock(block);
local scope = self.activeBlock.scope;
self:pushRegisterUsageInfo();
for i, arg in ipairs(node.args) do
if(arg.kind == AstKind.VariableExpression) then
if(self:isUpvalue(arg.scope, arg.id)) then
scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);
local argReg = self:getVarRegister(arg.scope, arg.id, funcDepth, nil);
self:addStatement(self:setRegister(scope, argReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {argReg}, {}, false);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, argReg), Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.argsVar), Ast.NumberExpression(i))), {}, {argReg}, true);
else
local argReg = self:getVarRegister(arg.scope, arg.id, funcDepth, nil);
scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);
self:addStatement(self:setRegister(scope, argReg, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.argsVar), Ast.NumberExpression(i))), {argReg}, {}, false);
end
else
self.varargReg = self:allocRegister(true);
scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);
scope:addReferenceToHigherScope(self.scope, self.selectVar);
scope:addReferenceToHigherScope(self.scope, self.unpackVar);
self:addStatement(self:setRegister(scope, self.varargReg, Ast.TableConstructorExpression({
Ast.TableEntry(Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.selectVar), {
Ast.NumberExpression(i);
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.unpackVar), {
Ast.VariableExpression(self.containerFuncScope, self.argsVar),
});
})),
})), {self.varargReg}, {}, false);
end
end
self:compileBlock(node.body, funcDepth);
if(self.activeBlock.advanceToNextBlock) then
self:addStatement(self:setPos(self.activeBlock.scope, nil), {self.POS_REGISTER}, {}, false);
self:addStatement(self:setReturn(self.activeBlock.scope, Ast.TableConstructorExpression({})), {self.RETURN_REGISTER}, {}, false);
self.activeBlock.advanceToNextBlock = false;
end
if(self.varargReg) then
self:freeRegister(self.varargReg, true);
end
self.varargReg = upperVarargReg;
self.getUpvalueId = oldGetUpvalueId;
self:popRegisterUsageInfo();
self:setActiveBlock(oldActiveBlock);
local scope = self.activeBlock.scope;
local retReg = self:allocRegister(false);
local isVarargFunction = #node.args > 0 and node.args[#node.args].kind == AstKind.VarargExpression;
local retrieveExpression
if isVarargFunction then
scope:addReferenceToHigherScope(self.scope, self.createVarargClosureVar);
retrieveExpression = Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.createVarargClosureVar), {
Ast.NumberExpression(block.id),
Ast.TableConstructorExpression(upvalueExpressions)
});
else
local varScope, var = self:getCreateClosureVar(#node.args + math.random(0, 5));
scope:addReferenceToHigherScope(varScope, var);
retrieveExpression = Ast.FunctionCallExpression(Ast.VariableExpression(varScope, var), {
Ast.NumberExpression(block.id),
Ast.TableConstructorExpression(upvalueExpressions)
});
end
self:addStatement(self:setRegister(scope, retReg, retrieveExpression), {retReg}, usedRegs, false);
return retReg;
end
function Compiler:compileBlock(block, funcDepth)
for i, stat in ipairs(block.statements) do
self:compileStatement(stat, funcDepth);
end
local scope = self.activeBlock.scope;
for id, name in ipairs(block.scope.variables) do
local varReg = self:getVarRegister(block.scope, id, funcDepth, nil);
if self:isUpvalue(block.scope, id) then
scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);
self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), {
self:register(scope, varReg)
})), {varReg}, {varReg}, false);
else
self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), {varReg}, {}, false);
end
self:freeRegister(varReg, true);
end
end
end
================================================
FILE: src/prometheus/compiler/compiler.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- compiler.lua
--
-- This Script is the main compiler module.
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local util = require("prometheus.util");
local lookupify = util.lookupify;
local AstKind = Ast.AstKind;
local unpack = unpack or table.unpack;
local blockModule = require("prometheus.compiler.block");
local registerModule = require("prometheus.compiler.register");
local upvalueModule = require("prometheus.compiler.upvalue");
local emitModule = require("prometheus.compiler.emit");
local compileCoreModule = require("prometheus.compiler.compile_core");
local Compiler = {};
function Compiler:new()
local compiler = {
blocks = {};
registers = {};
activeBlock = nil;
registersForVar = {};
usedRegisters = 0;
maxUsedRegister = 0;
registerVars = {};
VAR_REGISTER = newproxy(false);
RETURN_ALL = newproxy(false);
POS_REGISTER = newproxy(false);
RETURN_REGISTER = newproxy(false);
UPVALUE = newproxy(false);
BIN_OPS = lookupify{
AstKind.LessThanExpression,
AstKind.GreaterThanExpression,
AstKind.LessThanOrEqualsExpression,
AstKind.GreaterThanOrEqualsExpression,
AstKind.NotEqualsExpression,
AstKind.EqualsExpression,
AstKind.StrCatExpression,
AstKind.AddExpression,
AstKind.SubExpression,
AstKind.MulExpression,
AstKind.DivExpression,
AstKind.ModExpression,
AstKind.PowExpression,
};
};
setmetatable(compiler, self);
self.__index = self;
return compiler;
end
blockModule(Compiler);
registerModule(Compiler);
upvalueModule(Compiler);
emitModule(Compiler);
compileCoreModule(Compiler);
function Compiler:pushRegisterUsageInfo()
table.insert(self.registerUsageStack, {
usedRegisters = self.usedRegisters;
registers = self.registers;
});
self.usedRegisters = 0;
self.registers = {};
end
function Compiler:popRegisterUsageInfo()
local info = table.remove(self.registerUsageStack);
self.usedRegisters = info.usedRegisters;
self.registers = info.registers;
end
function Compiler:compile(ast)
self.blocks = {};
self.registers = {};
self.activeBlock = nil;
self.registersForVar = {};
self.scopeFunctionDepths = {};
self.maxUsedRegister = 0;
self.usedRegisters = 0;
self.registerVars = {};
self.usedBlockIds = {};
self.upvalVars = {};
self.registerUsageStack = {};
self.upvalsProxyLenReturn = math.random(-2^22, 2^22);
local newGlobalScope = Scope:newGlobal();
local psc = Scope:new(newGlobalScope, nil);
local _, getfenvVar = newGlobalScope:resolve("getfenv");
local _, tableVar = newGlobalScope:resolve("table");
local _, unpackVar = newGlobalScope:resolve("unpack");
local _, envVar = newGlobalScope:resolve("_ENV");
local _, newproxyVar = newGlobalScope:resolve("newproxy");
local _, setmetatableVar = newGlobalScope:resolve("setmetatable");
local _, getmetatableVar = newGlobalScope:resolve("getmetatable");
local _, selectVar = newGlobalScope:resolve("select");
psc:addReferenceToHigherScope(newGlobalScope, getfenvVar, 2);
psc:addReferenceToHigherScope(newGlobalScope, tableVar);
psc:addReferenceToHigherScope(newGlobalScope, unpackVar);
psc:addReferenceToHigherScope(newGlobalScope, envVar);
psc:addReferenceToHigherScope(newGlobalScope, newproxyVar);
psc:addReferenceToHigherScope(newGlobalScope, setmetatableVar);
psc:addReferenceToHigherScope(newGlobalScope, getmetatableVar);
self.scope = Scope:new(psc);
self.envVar = self.scope:addVariable();
self.containerFuncVar = self.scope:addVariable();
self.unpackVar = self.scope:addVariable();
self.newproxyVar = self.scope:addVariable();
self.setmetatableVar = self.scope:addVariable();
self.getmetatableVar = self.scope:addVariable();
self.selectVar = self.scope:addVariable();
local argVar = self.scope:addVariable();
self.containerFuncScope = Scope:new(self.scope);
self.whileScope = Scope:new(self.containerFuncScope);
self.posVar = self.containerFuncScope:addVariable();
self.argsVar = self.containerFuncScope:addVariable();
self.currentUpvaluesVar = self.containerFuncScope:addVariable();
self.detectGcCollectVar = self.containerFuncScope:addVariable();
self.returnVar = self.containerFuncScope:addVariable();
self.upvaluesTable = self.scope:addVariable();
self.upvaluesReferenceCountsTable = self.scope:addVariable();
self.allocUpvalFunction = self.scope:addVariable();
self.currentUpvalId = self.scope:addVariable();
self.upvaluesProxyFunctionVar = self.scope:addVariable();
self.upvaluesGcFunctionVar = self.scope:addVariable();
self.freeUpvalueFunc = self.scope:addVariable();
self.createClosureVars = {};
self.createVarargClosureVar = self.scope:addVariable();
local createClosureScope = Scope:new(self.scope);
local createClosurePosArg = createClosureScope:addVariable();
local createClosureUpvalsArg = createClosureScope:addVariable();
local createClosureProxyObject = createClosureScope:addVariable();
local createClosureFuncVar = createClosureScope:addVariable();
local createClosureSubScope = Scope:new(createClosureScope);
local upvalEntries = {};
local upvalueIds = {};
self.getUpvalueId = function(self, scope, id)
local expression;
local scopeFuncDepth = self.scopeFunctionDepths[scope];
if(scopeFuncDepth == 0) then
if upvalueIds[id] then
return upvalueIds[id];
end
expression = Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {});
else
require("logger"):error("Unresolved Upvalue, this error should not occur!");
end
table.insert(upvalEntries, Ast.TableEntry(expression));
local uid = #upvalEntries;
upvalueIds[id] = uid;
return uid;
end
createClosureSubScope:addReferenceToHigherScope(self.scope, self.containerFuncVar);
createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosurePosArg)
createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureUpvalsArg, 1)
createClosureScope:addReferenceToHigherScope(self.scope, self.upvaluesProxyFunctionVar)
createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureProxyObject);
self:compileTopNode(ast);
local functionNodeAssignments = {
{
var = Ast.AssignmentVariable(self.scope, self.containerFuncVar),
val = Ast.FunctionLiteralExpression({
Ast.VariableExpression(self.containerFuncScope, self.posVar),
Ast.VariableExpression(self.containerFuncScope, self.argsVar),
Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar),
Ast.VariableExpression(self.containerFuncScope, self.detectGcCollectVar)
}, self:emitContainerFuncBody());
}, {
var = Ast.AssignmentVariable(self.scope, self.createVarargClosureVar),
val = Ast.FunctionLiteralExpression({
Ast.VariableExpression(createClosureScope, createClosurePosArg),
Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),
},
Ast.Block({
Ast.LocalVariableDeclaration(createClosureScope, {
createClosureProxyObject
}, {
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.upvaluesProxyFunctionVar), {
Ast.VariableExpression(createClosureScope, createClosureUpvalsArg)
})
}),
Ast.LocalVariableDeclaration(createClosureScope, {createClosureFuncVar},{
Ast.FunctionLiteralExpression({
Ast.VarargExpression();
},
Ast.Block({
Ast.ReturnStatement{
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.containerFuncVar), {
Ast.VariableExpression(createClosureScope, createClosurePosArg),
Ast.TableConstructorExpression({Ast.TableEntry(Ast.VarargExpression())}),
Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),
Ast.VariableExpression(createClosureScope, createClosureProxyObject)
})
}
}, createClosureSubScope)
);
});
Ast.ReturnStatement{Ast.VariableExpression(createClosureScope, createClosureFuncVar)};
}, createClosureScope)
);
}, {
var = Ast.AssignmentVariable(self.scope, self.upvaluesTable),
val = Ast.TableConstructorExpression({}),
}, {
var = Ast.AssignmentVariable(self.scope, self.upvaluesReferenceCountsTable),
val = Ast.TableConstructorExpression({}),
}, {
var = Ast.AssignmentVariable(self.scope, self.allocUpvalFunction),
val = self:createAllocUpvalFunction(),
}, {
var = Ast.AssignmentVariable(self.scope, self.currentUpvalId),
val = Ast.NumberExpression(0),
}, {
var = Ast.AssignmentVariable(self.scope, self.upvaluesProxyFunctionVar),
val = self:createUpvaluesProxyFunc(),
}, {
var = Ast.AssignmentVariable(self.scope, self.upvaluesGcFunctionVar),
val = self:createUpvaluesGcFunc(),
}, {
var = Ast.AssignmentVariable(self.scope, self.freeUpvalueFunc),
val = self:createFreeUpvalueFunc(),
},
}
local tbl = {
Ast.VariableExpression(self.scope, self.containerFuncVar),
Ast.VariableExpression(self.scope, self.createVarargClosureVar),
Ast.VariableExpression(self.scope, self.upvaluesTable),
Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable),
Ast.VariableExpression(self.scope, self.allocUpvalFunction),
Ast.VariableExpression(self.scope, self.currentUpvalId),
Ast.VariableExpression(self.scope, self.upvaluesProxyFunctionVar),
Ast.VariableExpression(self.scope, self.upvaluesGcFunctionVar),
Ast.VariableExpression(self.scope, self.freeUpvalueFunc),
};
for i, entry in pairs(self.createClosureVars) do
table.insert(functionNodeAssignments, entry);
table.insert(tbl, Ast.VariableExpression(entry.var.scope, entry.var.id));
end
util.shuffle(functionNodeAssignments);
local assignmentStatLhs, assignmentStatRhs = {}, {};
for i, v in ipairs(functionNodeAssignments) do
assignmentStatLhs[i] = v.var;
assignmentStatRhs[i] = v.val;
end
-- NEW: Position Shuffler
local ids = util.shuffle({1, 2, 3, 4, 5, 6, 7});
local items = {
Ast.VariableExpression(self.scope, self.envVar),
Ast.VariableExpression(self.scope, self.unpackVar),
Ast.VariableExpression(self.scope, self.newproxyVar),
Ast.VariableExpression(self.scope, self.setmetatableVar),
Ast.VariableExpression(self.scope, self.getmetatableVar),
Ast.VariableExpression(self.scope, self.selectVar),
Ast.VariableExpression(self.scope, argVar),
}
local astItems = {
Ast.OrExpression(Ast.AndExpression(Ast.VariableExpression(newGlobalScope, getfenvVar), Ast.FunctionCallExpression(Ast.VariableExpression(newGlobalScope, getfenvVar), {})), Ast.VariableExpression(newGlobalScope, envVar));
Ast.OrExpression(Ast.VariableExpression(newGlobalScope, unpackVar), Ast.IndexExpression(Ast.VariableExpression(newGlobalScope, tableVar), Ast.StringExpression("unpack")));
Ast.VariableExpression(newGlobalScope, newproxyVar);
Ast.VariableExpression(newGlobalScope, setmetatableVar);
Ast.VariableExpression(newGlobalScope, getmetatableVar);
Ast.VariableExpression(newGlobalScope, selectVar);
Ast.TableConstructorExpression({
Ast.TableEntry(Ast.VarargExpression());
})
}
local functionNode = Ast.FunctionLiteralExpression({
items[ids[1]], items[ids[2]], items[ids[3]], items[ids[4]],
items[ids[5]], items[ids[6]], items[ids[7]],
unpack(util.shuffle(tbl))
}, Ast.Block({
Ast.AssignmentStatement(assignmentStatLhs, assignmentStatRhs);
Ast.ReturnStatement{
Ast.FunctionCallExpression(Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.createVarargClosureVar), {
Ast.NumberExpression(self.startBlockId);
Ast.TableConstructorExpression(upvalEntries);
}), {Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.unpackVar), {Ast.VariableExpression(self.scope, argVar)})});
}
}, self.scope));
return Ast.TopNode(Ast.Block({
Ast.ReturnStatement{Ast.FunctionCallExpression(functionNode, {
astItems[ids[1]], astItems[ids[2]], astItems[ids[3]], astItems[ids[4]],
astItems[ids[5]], astItems[ids[6]], astItems[ids[7]],
})};
}, psc), newGlobalScope);
end
function Compiler:getCreateClosureVar(argCount)
if not self.createClosureVars[argCount] then
local var = Ast.AssignmentVariable(self.scope, self.scope:addVariable());
local createClosureScope = Scope:new(self.scope);
local createClosureSubScope = Scope:new(createClosureScope);
local createClosurePosArg = createClosureScope:addVariable();
local createClosureUpvalsArg = createClosureScope:addVariable();
local createClosureProxyObject = createClosureScope:addVariable();
local createClosureFuncVar = createClosureScope:addVariable();
createClosureSubScope:addReferenceToHigherScope(self.scope, self.containerFuncVar);
createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosurePosArg)
createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureUpvalsArg, 1)
createClosureScope:addReferenceToHigherScope(self.scope, self.upvaluesProxyFunctionVar)
createClosureSubScope:addReferenceToHigherScope(createClosureScope, createClosureProxyObject);
local argsTb, argsTb2 = {}, {};
for i = 1, argCount do
local arg = createClosureSubScope:addVariable()
argsTb[i] = Ast.VariableExpression(createClosureSubScope, arg);
argsTb2[i] = Ast.TableEntry(Ast.VariableExpression(createClosureSubScope, arg));
end
local val = Ast.FunctionLiteralExpression({
Ast.VariableExpression(createClosureScope, createClosurePosArg),
Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),
}, Ast.Block({
Ast.LocalVariableDeclaration(createClosureScope, {
createClosureProxyObject
}, {
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.upvaluesProxyFunctionVar), {
Ast.VariableExpression(createClosureScope, createClosureUpvalsArg)
})
}),
Ast.LocalVariableDeclaration(createClosureScope, {createClosureFuncVar},{
Ast.FunctionLiteralExpression(argsTb,
Ast.Block({
Ast.ReturnStatement{
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.containerFuncVar), {
Ast.VariableExpression(createClosureScope, createClosurePosArg),
Ast.TableConstructorExpression(argsTb2),
Ast.VariableExpression(createClosureScope, createClosureUpvalsArg),
Ast.VariableExpression(createClosureScope, createClosureProxyObject)
})
}
}, createClosureSubScope)
);
});
Ast.ReturnStatement{Ast.VariableExpression(createClosureScope, createClosureFuncVar)}
}, createClosureScope)
);
self.createClosureVars[argCount] = {
var = var,
val = val,
}
end
local var = self.createClosureVars[argCount].var;
return var.scope, var.id;
end
return Compiler;
================================================
FILE: src/prometheus/compiler/constants.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- constants.lua
--
-- This Script contains the compiler constants shared across modules.
return {
MAX_REGS = 100,
MAX_REGS_MUL = 0,
}
================================================
FILE: src/prometheus/compiler/emit.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- emit.lua
--
-- This Script contains the container function body emission for the compiler.
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local util = require("prometheus.util");
local constants = require("prometheus.compiler.constants");
local AstKind = Ast.AstKind;
local MAX_REGS = constants.MAX_REGS;
return function(Compiler)
local function hasAnyEntries(tbl)
return type(tbl) == "table" and next(tbl) ~= nil;
end
local function unionLookupTables(a, b)
local out = {};
for k, v in pairs(a or {}) do
out[k] = v;
end
for k, v in pairs(b or {}) do
out[k] = v;
end
return out;
end
local function canMergeParallelAssignmentStatements(statA, statB)
if type(statA) ~= "table" or type(statB) ~= "table" then
return false;
end
if statA.usesUpvals or statB.usesUpvals then
return false;
end
local a = statA.statement;
local b = statB.statement;
if type(a) ~= "table" or type(b) ~= "table" then
return false;
end
if a.kind ~= AstKind.AssignmentStatement or b.kind ~= AstKind.AssignmentStatement then
return false;
end
if type(a.lhs) ~= "table" or type(a.rhs) ~= "table" or type(b.lhs) ~= "table" or type(b.rhs) ~= "table" then
return false;
end
if #a.lhs ~= #a.rhs or #b.lhs ~= #b.rhs then
return false;
end
-- Avoid merging vararg/call assignments because they can affect multi-return behavior.
local function hasUnsafeRhs(rhsList)
for _, rhsExpr in ipairs(rhsList) do
if type(rhsExpr) ~= "table" then
return true;
end
local kind = rhsExpr.kind;
if kind == AstKind.FunctionCallExpression or kind == AstKind.PassSelfFunctionCallExpression or kind == AstKind.VarargExpression then
return true;
end
end
return false;
end
if hasUnsafeRhs(a.rhs) or hasUnsafeRhs(b.rhs) then
return false;
end
local aReads = type(statA.reads) == "table" and statA.reads or {};
local aWrites = type(statA.writes) == "table" and statA.writes or {};
local bReads = type(statB.reads) == "table" and statB.reads or {};
local bWrites = type(statB.writes) == "table" and statB.writes or {};
-- Allow merging even if one statement has no writes (e.g., x = o(x) style assignments)
-- Only require that at least one of them has writes
if not hasAnyEntries(aWrites) and not hasAnyEntries(bWrites) then
return false;
end
for r in pairs(aReads) do
if bWrites[r] then
return false;
end
end
for r, b in pairs(aWrites) do
if bWrites[r] or bReads[r] then
return false;
end
end
return true;
end
local function mergeParallelAssignmentStatements(statA, statB)
local lhs = {};
local rhs = {};
local aLhs, bLhs = statA.statement.lhs, statB.statement.lhs;
local aRhs, bRhs = statA.statement.rhs, statB.statement.rhs;
for i = 1, #aLhs do lhs[i] = aLhs[i]; end
for i = 1, #bLhs do lhs[#aLhs + i] = bLhs[i]; end
for i = 1, #aRhs do rhs[i] = aRhs[i]; end
for i = 1, #bRhs do rhs[#aRhs + i] = bRhs[i]; end
return {
statement = Ast.AssignmentStatement(lhs, rhs),
writes = unionLookupTables(statA.writes, statB.writes),
reads = unionLookupTables(statA.reads, statB.reads),
usesUpvals = statA.usesUpvals or statB.usesUpvals,
};
end
local function mergeAdjacentParallelAssignments(blockstats)
local merged = {};
local i = 1;
while i <= #blockstats do
local stat = blockstats[i];
i = i + 1;
while i <= #blockstats and canMergeParallelAssignmentStatements(stat, blockstats[i]) do
stat = mergeParallelAssignmentStatements(stat, blockstats[i]);
i = i + 1;
end
table.insert(merged, stat);
end
return merged;
end
function Compiler:emitContainerFuncBody()
local blocks = {};
util.shuffle(self.blocks);
for i, block in ipairs(self.blocks) do
local id = block.id;
local blockstats = block.statements;
for i = 2, #blockstats do
local stat = blockstats[i];
local reads = stat.reads;
local writes = stat.writes;
local maxShift = 0;
local usesUpvals = stat.usesUpvals;
for shift = 1, i - 1 do
local stat2 = blockstats[i - shift];
if stat2.usesUpvals and usesUpvals then
break;
end
local reads2 = stat2.reads;
local writes2 = stat2.writes;
local f = true;
for r, b in pairs(reads2) do
if(writes[r]) then
f = false;
break;
end
end
if f then
for r, b in pairs(writes2) do
if(writes[r]) then
f = false;
break;
end
if(reads[r]) then
f = false;
break;
end
end
end
if not f then
break
end
maxShift = shift;
end
local shift = math.random(0, maxShift);
for j = 1, shift do
blockstats[i - j], blockstats[i - j + 1] = blockstats[i - j + 1], blockstats[i - j];
end
end
local mergedBlockStats = mergeAdjacentParallelAssignments(blockstats);
for _=1, 7 do
mergedBlockStats = mergeAdjacentParallelAssignments(mergedBlockStats);
end
blockstats = {};
for _, stat in ipairs(mergedBlockStats) do
table.insert(blockstats, stat.statement);
end
local block = { id = id, index = i, block = Ast.Block(blockstats, block.scope) }
table.insert(blocks, block);
blocks[id] = block;
end
table.sort(blocks, function(a, b) return a.id < b.id end);
-- Build a strict threshold condition between adjacent block IDs.
-- Using a midpoint avoids exact-id comparisons while preserving dispatch.
local function buildBlockThresholdCondition(scope, leftId, rightId, useAndOr)
local bound = math.floor((leftId + rightId) / 2);
local posExpr = self:pos(scope);
local boundExpr = Ast.NumberExpression(bound);
if useAndOr then
-- Kept for compatibility with caller variations.
return Ast.LessThanExpression(posExpr, boundExpr);
else
local variant = math.random(1, 2);
if variant == 1 then
return Ast.LessThanExpression(posExpr, boundExpr);
else
return Ast.GreaterThanExpression(boundExpr, posExpr);
end
end
end
-- Build an elseif chain for a range of blocks
local function buildElseifChain(tb, l, r, pScope)
-- Handle invalid range by returning an empty block
if r < l then
local emptyScope = Scope:new(pScope);
return Ast.Block({}, emptyScope);
end
local len = r - l + 1;
-- For single block
if len == 1 then
tb[l].block.scope:setParent(pScope);
return tb[l].block;
end
-- For small ranges, use elseif chain
if len <= 4 then
local ifScope = Scope:new(pScope);
local elseifs = {};
-- First block uses the first midpoint threshold
tb[l].block.scope:setParent(ifScope);
local firstCondition = buildBlockThresholdCondition(ifScope, tb[l].id, tb[l + 1].id, false);
local firstBlock = tb[l].block;
-- Middle blocks use their upper midpoint threshold
for i = l + 1, r - 1 do
tb[i].block.scope:setParent(ifScope);
local condition = buildBlockThresholdCondition(ifScope, tb[i].id, tb[i + 1].id, false);
table.insert(elseifs, {
condition = condition,
body = tb[i].block
});
end
-- Last block becomes else
tb[r].block.scope:setParent(ifScope);
local elseBlock = tb[r].block;
return Ast.Block({
Ast.IfStatement(firstCondition, firstBlock, elseifs, elseBlock);
}, ifScope);
end
-- For larger ranges, use binary split with and/or chaining
local mid = l + math.ceil(len / 2);
local leftMaxId = tb[mid - 1].id;
local rightMinId = tb[mid].id;
-- Float-safe split: any bound strictly between adjacent IDs works.
-- Midpoint avoids integer-only math.random(min, max) behavior.
local bound = math.floor((leftMaxId + rightMinId) / 2);
local ifScope = Scope:new(pScope);
local lBlock = buildElseifChain(tb, l, mid - 1, ifScope);
local rBlock = buildElseifChain(tb, mid, r, ifScope);
-- Randomly choose between different condition styles
local condStyle = math.random(1, 3);
local condition;
local trueBlock, falseBlock;
if condStyle == 1 then
-- pos < bound
condition = Ast.LessThanExpression(self:pos(ifScope), Ast.NumberExpression(bound));
trueBlock, falseBlock = lBlock, rBlock;
elseif condStyle == 2 then
-- bound > pos
condition = Ast.GreaterThanExpression(Ast.NumberExpression(bound), self:pos(ifScope));
trueBlock, falseBlock = lBlock, rBlock;
else
-- Equivalent split using strict > with branches reversed.
condition = Ast.GreaterThanExpression(self:pos(ifScope), Ast.NumberExpression(bound));
trueBlock, falseBlock = rBlock, lBlock;
end
return Ast.Block({
Ast.IfStatement(condition, trueBlock, {}, falseBlock);
}, ifScope);
end
local whileBody = buildElseifChain(blocks, 1, #blocks, self.containerFuncScope);
if self.whileScope then
-- Ensure whileScope is properly connected
self.whileScope:setParent(self.containerFuncScope);
end
self.whileScope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar, 1);
self.whileScope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);
self.containerFuncScope:addReferenceToHigherScope(self.scope, self.unpackVar);
local declarations = {
self.returnVar,
}
for i, var in pairs(self.registerVars) do
if(i ~= MAX_REGS) then
table.insert(declarations, var);
end
end
local stats = {}
if self.maxUsedRegister >= MAX_REGS then
table.insert(stats, Ast.LocalVariableDeclaration(self.containerFuncScope, {self.registerVars[MAX_REGS]}, {Ast.TableConstructorExpression({})}));
end
table.insert(stats, Ast.LocalVariableDeclaration(self.containerFuncScope, util.shuffle(declarations), {}));
table.insert(stats, Ast.WhileStatement(whileBody, Ast.VariableExpression(self.containerFuncScope, self.posVar)));
table.insert(stats, Ast.AssignmentStatement({
Ast.AssignmentVariable(self.containerFuncScope, self.posVar)
}, {
Ast.LenExpression(Ast.VariableExpression(self.containerFuncScope, self.detectGcCollectVar))
}));
table.insert(stats, Ast.ReturnStatement{
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.unpackVar), {
Ast.VariableExpression(self.containerFuncScope, self.returnVar)
});
});
return Ast.Block(stats, self.containerFuncScope);
end
end
================================================
FILE: src/prometheus/compiler/expressions/and.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- and.lua
--
-- This Script contains the expression handler for the AndExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local posState = self.registers[self.POS_REGISTER];
self.registers[self.POS_REGISTER] = self.VAR_REGISTER;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i ~= 1 then
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
local resReg = regs[1];
local tmpReg;
if posState then
tmpReg = self:allocRegister(false);
self:addStatement(self:copyRegisters(scope, {tmpReg}, {self.POS_REGISTER}), {tmpReg}, {self.POS_REGISTER}, false);
end
local lhsReg = self:compileExpression(expression.lhs, funcDepth, 1)[1];
if expression.rhs.isConstant then
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, resReg, Ast.AndExpression(self:register(scope, lhsReg), self:register(scope, rhsReg))), {resReg}, {lhsReg, rhsReg}, false);
if tmpReg then
self:freeRegister(tmpReg, false);
end
self:freeRegister(lhsReg, false);
self:freeRegister(rhsReg, false);
return regs;
end
local block1, block2 = self:createBlock(), self:createBlock();
self:addStatement(self:copyRegisters(scope, {resReg}, {lhsReg}), {resReg}, {lhsReg}, false);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, lhsReg), Ast.NumberExpression(block1.id)), Ast.NumberExpression(block2.id))), {self.POS_REGISTER}, {lhsReg}, false);
self:freeRegister(lhsReg, false);
do
self:setActiveBlock(block1);
scope = block1.scope;
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
self:addStatement(self:copyRegisters(scope, {resReg}, {rhsReg}), {resReg}, {rhsReg}, false);
self:freeRegister(rhsReg, false);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(block2.id)), {self.POS_REGISTER}, {}, false);
end
self.registers[self.POS_REGISTER] = posState;
self:setActiveBlock(block2);
scope = block2.scope;
if tmpReg then
self:addStatement(self:copyRegisters(scope, {self.POS_REGISTER}, {tmpReg}), {self.POS_REGISTER}, {tmpReg}, false);
self:freeRegister(tmpReg, false);
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/binary.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- binary.lua
--
-- This Script contains the expression handler for the Binary operations (Add, Sub, Mul, Div, etc.).
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
local lhsReg = self:compileExpression(expression.lhs, funcDepth, 1)[1];
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
local binaryExpr = Ast[expression.kind](self:register(scope, lhsReg), self:register(scope, rhsReg));
self:addStatement(self:setRegister(scope, regs[i], binaryExpr), {regs[i]}, {lhsReg, rhsReg}, true);
self:freeRegister(rhsReg, false);
self:freeRegister(lhsReg, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/boolean.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- boolean.lua
--
-- This Script contains the expression handler for the BooleanExpression.
local Ast = require("prometheus.ast");
local expressionEvaluators = {
[Ast.GreaterThanExpression] = function(left, right)
return left > right
end,
[Ast.LessThanExpression] = function(left, right)
return left < right
end,
[Ast.GreaterThanOrEqualsExpression] = function(left, right)
return left >= right
end,
[Ast.LessThanOrEqualsExpression] = function(left, right)
return left <= right
end,
[Ast.NotEqualsExpression] = function(left, right)
return left ~= right
end,
}
local function createRandomASTCFlowExpression(resultBool)
local expTB = {
Ast.GreaterThanExpression,
Ast.LessThanExpression,
Ast.GreaterThanOrEqualsExpression,
Ast.LessThanOrEqualsExpression,
Ast.NotEqualsExpression
}
local leftInt, rightInt, boolResult, randomExp
repeat
randomExp = expTB[math.random(1, #expTB)]
leftInt = Ast.NumberExpression(math.random(1, 2^24))
rightInt = Ast.NumberExpression(math.random(1, 2^24))
boolResult = expressionEvaluators[randomExp](leftInt.value, rightInt.value)
until boolResult == resultBool
return randomExp(leftInt, rightInt, false)
end
return function(self, expression, _, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
self:addStatement(self:setRegister(scope, regs[i], createRandomASTCFlowExpression(expression.value)), {regs[i]}, {}, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/function_call.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- function_call.lua
--
-- This Script contains the expression handler for the FunctionCallExpression.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local baseReg = self:compileExpression(expression.base, funcDepth, 1)[1];
local retRegs = {};
local returnAll = numReturns == self.RETURN_ALL;
if returnAll then
retRegs[1] = self:allocRegister(false);
else
for i = 1, numReturns do
retRegs[i] = self:allocRegister(false);
end
end
local regs = {};
local args = {};
for i, expr in ipairs(expression.args) do
if i == #expression.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then
local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];
table.insert(args, Ast.FunctionCallExpression(
self:unpack(scope),
{self:register(scope, reg)}));
table.insert(regs, reg);
else
local reg = self:compileExpression(expr, funcDepth, 1)[1];
table.insert(args, self:register(scope, reg));
table.insert(regs, reg);
end
end
if returnAll then
self:addStatement(self:setRegister(scope, retRegs[1], Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, baseReg), args))}), {retRegs[1]}, {baseReg, unpack(regs)}, true);
else
if numReturns > 1 then
local tmpReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, baseReg), args))}), {tmpReg}, {baseReg, unpack(regs)}, true);
for i, reg in ipairs(retRegs) do
self:addStatement(self:setRegister(scope, reg, Ast.IndexExpression(self:register(scope, tmpReg), Ast.NumberExpression(i))), {reg}, {tmpReg}, false);
end
self:freeRegister(tmpReg, false);
else
self:addStatement(self:setRegister(scope, retRegs[1], Ast.FunctionCallExpression(self:register(scope, baseReg), args)), {retRegs[1]}, {baseReg, unpack(regs)}, true);
end
end
self:freeRegister(baseReg, false);
for i, reg in ipairs(regs) do
self:freeRegister(reg, false);
end
return retRegs;
end;
================================================
FILE: src/prometheus/compiler/expressions/function_literal.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- function_literal.lua
--
-- This Script contains the expression handler for the FunctionLiteralExpression
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
if i == 1 then
regs[i] = self:compileFunction(expression, funcDepth);
else
regs[i] = self:allocRegister();
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/index.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- index.lua
--
-- This Script contains the expression handler for the IndexExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
local baseReg = self:compileExpression(expression.base, funcDepth, 1)[1];
local indexReg = self:compileExpression(expression.index, funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, regs[i], Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, indexReg))), {regs[i]}, {baseReg, indexReg}, true);
self:freeRegister(baseReg, false);
self:freeRegister(indexReg, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/len.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- len.lua
--
-- This Script contains the expression handler for the LenExpression
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, regs[i], Ast.LenExpression(self:register(scope, rhsReg))), {regs[i]}, {rhsReg}, true);
self:freeRegister(rhsReg, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/negate.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- negate.lua
--
-- This Script contains the expression handler for the NegateExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, regs[i], Ast.NegateExpression(self:register(scope, rhsReg))), {regs[i]}, {rhsReg}, true);
self:freeRegister(rhsReg, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/nil.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- nil.lua
--
-- This Script contains the expression handler for the NilExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/not.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- not.lua
--
-- This Script contains the expression handler for the NotExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, regs[i], Ast.NotExpression(self:register(scope, rhsReg))), {regs[i]}, {rhsReg}, false);
self:freeRegister(rhsReg, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/number.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- number.lua
--
-- This Script contains the expression handler for the NumberExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
self:addStatement(self:setRegister(scope, regs[i], Ast.NumberExpression(expression.value)), {regs[i]}, {}, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/or.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- or.lua
--
-- This Script contains the expression handler for the OrExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local posState = self.registers[self.POS_REGISTER];
self.registers[self.POS_REGISTER] = self.VAR_REGISTER;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i ~= 1 then
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
local resReg = regs[1];
local tmpReg;
if posState then
tmpReg = self:allocRegister(false);
self:addStatement(self:copyRegisters(scope, {tmpReg}, {self.POS_REGISTER}), {tmpReg}, {self.POS_REGISTER}, false);
end
local lhsReg = self:compileExpression(expression.lhs, funcDepth, 1)[1];
if expression.rhs.isConstant then
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, resReg, Ast.OrExpression(self:register(scope, lhsReg), self:register(scope, rhsReg))), {resReg}, {lhsReg, rhsReg}, false);
if tmpReg then
self:freeRegister(tmpReg, false);
end
self:freeRegister(lhsReg, false);
self:freeRegister(rhsReg, false);
return regs;
end
local block1, block2 = self:createBlock(), self:createBlock();
self:addStatement(self:copyRegisters(scope, {resReg}, {lhsReg}), {resReg}, {lhsReg}, false);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, lhsReg), Ast.NumberExpression(block2.id)), Ast.NumberExpression(block1.id))), {self.POS_REGISTER}, {lhsReg}, false);
self:freeRegister(lhsReg, false);
do
self:setActiveBlock(block1);
local scope = block1.scope;
local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1];
self:addStatement(self:copyRegisters(scope, {resReg}, {rhsReg}), {resReg}, {rhsReg}, false);
self:freeRegister(rhsReg, false);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(block2.id)), {self.POS_REGISTER}, {}, false);
end
self.registers[self.POS_REGISTER] = posState;
self:setActiveBlock(block2);
scope = block2.scope;
if tmpReg then
self:addStatement(self:copyRegisters(scope, {self.POS_REGISTER}, {tmpReg}), {self.POS_REGISTER}, {tmpReg}, false);
self:freeRegister(tmpReg, false);
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/pass_self_function_call.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- pass_self_function_call.lua
--
-- This Script contains the expression handler for the PassSelfFunctionCallExpression.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local baseReg = self:compileExpression(expression.base, funcDepth, 1)[1];
local retRegs = {};
local returnAll = numReturns == self.RETURN_ALL;
if returnAll then
retRegs[1] = self:allocRegister(false);
else
for i = 1, numReturns do
retRegs[i] = self:allocRegister(false);
end
end
local args = { self:register(scope, baseReg) };
local regs = { baseReg };
for i, expr in ipairs(expression.args) do
if i == #expression.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then
local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];
table.insert(args, Ast.FunctionCallExpression(
self:unpack(scope),
{self:register(scope, reg)}));
table.insert(regs, reg);
else
local reg = self:compileExpression(expr, funcDepth, 1)[1];
table.insert(args, self:register(scope, reg));
table.insert(regs, reg);
end
end
if returnAll or numReturns > 1 then
local tmpReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(expression.passSelfFunctionName)), {tmpReg}, {}, false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, tmpReg))), {tmpReg}, {baseReg, tmpReg}, false);
if returnAll then
self:addStatement(self:setRegister(scope, retRegs[1], Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, tmpReg), args))}), {retRegs[1]}, {tmpReg, unpack(regs)}, true);
else
self:addStatement(self:setRegister(scope, tmpReg, Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, tmpReg), args))}), {tmpReg}, {tmpReg, unpack(regs)}, true);
for i, reg in ipairs(retRegs) do
self:addStatement(self:setRegister(scope, reg, Ast.IndexExpression(self:register(scope, tmpReg), Ast.NumberExpression(i))), {reg}, {tmpReg}, false);
end
end
self:freeRegister(tmpReg, false);
else
local tmpReg = retRegs[1] or self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(expression.passSelfFunctionName)), {tmpReg}, {}, false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, tmpReg))), {tmpReg}, {baseReg, tmpReg}, false);
self:addStatement(self:setRegister(scope, retRegs[1], Ast.FunctionCallExpression(self:register(scope, tmpReg), args)), {retRegs[1]}, {baseReg, unpack(regs)}, true);
end
for i, reg in ipairs(regs) do
self:freeRegister(reg, false);
end
return retRegs;
end;
================================================
FILE: src/prometheus/compiler/expressions/string.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- string.lua
--
-- This Script contains the expression handler for the StringExpression.
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns, 1 do
regs[i] = self:allocRegister();
if i == 1 then
self:addStatement(self:setRegister(scope, regs[i], Ast.StringExpression(expression.value)), {regs[i]}, {}, false);
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/table_constructor.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- table_constructor.lua
--
-- This Script contains the expression handler for the TableConstructorExpression.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister();
if i == 1 then
local entries = {};
local entryRegs = {};
for i, entry in ipairs(expression.entries) do
if entry.kind == AstKind.TableEntry then
local value = entry.value;
if i == #expression.entries and (value.kind == AstKind.FunctionCallExpression or value.kind == AstKind.PassSelfFunctionCallExpression or value.kind == AstKind.VarargExpression) then
local reg = self:compileExpression(entry.value, funcDepth, self.RETURN_ALL)[1];
table.insert(entries, Ast.TableEntry(Ast.FunctionCallExpression(
self:unpack(scope),
{self:register(scope, reg)})));
table.insert(entryRegs, reg);
else
local reg = self:compileExpression(entry.value, funcDepth, 1)[1];
table.insert(entries, Ast.TableEntry(self:register(scope, reg)));
table.insert(entryRegs, reg);
end
else
local keyReg = self:compileExpression(entry.key, funcDepth, 1)[1];
local valReg = self:compileExpression(entry.value, funcDepth, 1)[1];
table.insert(entries, Ast.KeyedTableEntry(self:register(scope, keyReg), self:register(scope, valReg)));
table.insert(entryRegs, valReg);
table.insert(entryRegs, keyReg);
end
end
self:addStatement(self:setRegister(scope, regs[i], Ast.TableConstructorExpression(entries)), {regs[i]}, entryRegs, false);
for i, reg in ipairs(entryRegs) do
self:freeRegister(reg, false);
end
else
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/vararg.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- vararg.lua
--
-- This Script contains the expression handler for the VarargExpression
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
if numReturns == self.RETURN_ALL then
return {self.varargReg};
end
local regs = {};
for i = 1, numReturns do
regs[i] = self:allocRegister(false);
self:addStatement(self:setRegister(scope, regs[i], Ast.IndexExpression(self:register(scope, self.varargReg), Ast.NumberExpression(i))), {regs[i]}, {self.varargReg}, false);
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions/variable.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- variable.lua
--
-- This Script contains the expression handler for the VariableExpression
local Ast = require("prometheus.ast");
return function(self, expression, funcDepth, numReturns)
local scope = self.activeBlock.scope;
local regs = {};
for i = 1, numReturns do
if i == 1 then
if expression.scope.isGlobal then
regs[i] = self:allocRegister(false);
local tmpReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(expression.scope:getVariableName(expression.id))), {tmpReg}, {}, false);
self:addStatement(self:setRegister(scope, regs[i], Ast.IndexExpression(self:env(scope), self:register(scope, tmpReg))), {regs[i]}, {tmpReg}, true);
self:freeRegister(tmpReg, false);
else
if self.scopeFunctionDepths[expression.scope] == funcDepth then
if self:isUpvalue(expression.scope, expression.id) then
local reg = self:allocRegister(false);
local varReg = self:getVarRegister(expression.scope, expression.id, funcDepth, nil);
self:addStatement(self:setRegister(scope, reg, self:getUpvalueMember(scope, self:register(scope, varReg))), {reg}, {varReg}, true);
regs[i] = reg;
else
regs[i] = self:getVarRegister(expression.scope, expression.id, funcDepth, nil);
end
else
local reg = self:allocRegister(false);
local upvalId = self:getUpvalueId(expression.scope, expression.id);
scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);
self:addStatement(self:setRegister(scope, reg, self:getUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)))), {reg}, {}, true);
regs[i] = reg;
end
end
else
regs[i] = self:allocRegister();
self:addStatement(self:setRegister(scope, regs[i], Ast.NilExpression()), {regs[i]}, {}, false);
end
end
return regs;
end;
================================================
FILE: src/prometheus/compiler/expressions.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- expressions.lua
--
-- This Script contains the expression handlers: exports handler table keyed by AstKind.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
local handlers = {};
local expressions = "prometheus.compiler.expressions.";
local function requireExpression(name)
return require(expressions .. name);
end
handlers[AstKind.StringExpression] = requireExpression("string");
handlers[AstKind.NumberExpression] = requireExpression("number");
handlers[AstKind.BooleanExpression] = requireExpression("boolean");
handlers[AstKind.NilExpression] = requireExpression("nil");
handlers[AstKind.VariableExpression] = requireExpression("variable");
handlers[AstKind.FunctionCallExpression] = requireExpression("function_call");
handlers[AstKind.PassSelfFunctionCallExpression] = requireExpression("pass_self_function_call");
handlers[AstKind.IndexExpression] = requireExpression("index");
handlers[AstKind.NotExpression] = requireExpression("not");
handlers[AstKind.NegateExpression] = requireExpression("negate");
handlers[AstKind.LenExpression] = requireExpression("len");
handlers[AstKind.OrExpression] = requireExpression("or");
handlers[AstKind.AndExpression] = requireExpression("and");
handlers[AstKind.TableConstructorExpression] = requireExpression("table_constructor");
handlers[AstKind.FunctionLiteralExpression] = requireExpression("function_literal");
handlers[AstKind.VarargExpression] = requireExpression("vararg");
-- Binary ops share one handler
local binaryHandler = requireExpression("binary");
handlers[AstKind.LessThanExpression] = binaryHandler;
handlers[AstKind.GreaterThanExpression] = binaryHandler;
handlers[AstKind.LessThanOrEqualsExpression] = binaryHandler;
handlers[AstKind.GreaterThanOrEqualsExpression] = binaryHandler;
handlers[AstKind.NotEqualsExpression] = binaryHandler;
handlers[AstKind.EqualsExpression] = binaryHandler;
handlers[AstKind.StrCatExpression] = binaryHandler;
handlers[AstKind.AddExpression] = binaryHandler;
handlers[AstKind.SubExpression] = binaryHandler;
handlers[AstKind.MulExpression] = binaryHandler;
handlers[AstKind.DivExpression] = binaryHandler;
handlers[AstKind.ModExpression] = binaryHandler;
handlers[AstKind.PowExpression] = binaryHandler;
return handlers;
================================================
FILE: src/prometheus/compiler/register.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- register.lua
--
-- This Script contains the register management for the compiler.
local Ast = require("prometheus.ast");
local constants = require("prometheus.compiler.constants");
local randomStrings = require("prometheus.randomStrings");
local MAX_REGS = constants.MAX_REGS;
return function(Compiler)
function Compiler:freeRegister(id, force)
if force or not (self.registers[id] == self.VAR_REGISTER) then
self.usedRegisters = self.usedRegisters - 1;
self.registers[id] = false
end
end
function Compiler:isVarRegister(id)
return self.registers[id] == self.VAR_REGISTER;
end
function Compiler:allocRegister(isVar)
self.usedRegisters = self.usedRegisters + 1;
if not isVar then
if not self.registers[self.POS_REGISTER] then
self.registers[self.POS_REGISTER] = true;
return self.POS_REGISTER;
end
if not self.registers[self.RETURN_REGISTER] then
self.registers[self.RETURN_REGISTER] = true;
return self.RETURN_REGISTER;
end
end
local id = 0;
if self.usedRegisters < MAX_REGS * constants.MAX_REGS_MUL then
repeat
id = math.random(1, MAX_REGS - 1);
until not self.registers[id];
else
repeat
id = id + 1;
until not self.registers[id];
end
if id > self.maxUsedRegister then
self.maxUsedRegister = id;
end
if(isVar) then
self.registers[id] = self.VAR_REGISTER;
else
self.registers[id] = true
end
return id;
end
function Compiler:isUpvalue(scope, id)
return self.upvalVars[scope] and self.upvalVars[scope][id];
end
function Compiler:makeUpvalue(scope, id)
if(not self.upvalVars[scope]) then
self.upvalVars[scope] = {}
end
self.upvalVars[scope][id] = true;
end
function Compiler:getVarRegister(scope, id, functionDepth, potentialId)
if(not self.registersForVar[scope]) then
self.registersForVar[scope] = {};
self.scopeFunctionDepths[scope] = functionDepth;
end
local reg = self.registersForVar[scope][id];
if not reg then
if potentialId and self.registers[potentialId] ~= self.VAR_REGISTER and potentialId ~= self.POS_REGISTER and potentialId ~= self.RETURN_REGISTER then
self.registers[potentialId] = self.VAR_REGISTER;
reg = potentialId;
else
reg = self:allocRegister(true);
end
self.registersForVar[scope][id] = reg;
end
return reg;
end
function Compiler:getRegisterVarId(id)
local varId = self.registerVars[id];
if not varId then
varId = self.containerFuncScope:addVariable();
self.registerVars[id] = varId;
end
return varId;
end
function Compiler:register(scope, id)
if id == self.POS_REGISTER then
return self:pos(scope);
end
if id == self.RETURN_REGISTER then
return self:getReturn(scope);
end
if id < MAX_REGS then
local vid = self:getRegisterVarId(id);
scope:addReferenceToHigherScope(self.containerFuncScope, vid);
return Ast.VariableExpression(self.containerFuncScope, vid);
end
local vid = self:getRegisterVarId(MAX_REGS);
scope:addReferenceToHigherScope(self.containerFuncScope, vid);
return Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, vid), Ast.NumberExpression((id - MAX_REGS) + 1));
end
function Compiler:registerList(scope, ids)
local l = {};
for i, id in ipairs(ids) do
table.insert(l, self:register(scope, id));
end
return l;
end
function Compiler:registerAssignment(scope, id)
if id == self.POS_REGISTER then
return self:posAssignment(scope);
end
if id == self.RETURN_REGISTER then
return self:returnAssignment(scope);
end
if id < MAX_REGS then
local vid = self:getRegisterVarId(id);
scope:addReferenceToHigherScope(self.containerFuncScope, vid);
return Ast.AssignmentVariable(self.containerFuncScope, vid);
end
local vid = self:getRegisterVarId(MAX_REGS);
scope:addReferenceToHigherScope(self.containerFuncScope, vid);
return Ast.AssignmentIndexing(Ast.VariableExpression(self.containerFuncScope, vid), Ast.NumberExpression((id - MAX_REGS) + 1));
end
function Compiler:setRegister(scope, id, val, compundArg)
if(compundArg) then
return compundArg(self:registerAssignment(scope, id), val);
end
return Ast.AssignmentStatement({
self:registerAssignment(scope, id)
}, {
val
});
end
function Compiler:setRegisters(scope, ids, vals)
local idStats = {};
for i, id in ipairs(ids) do
table.insert(idStats, self:registerAssignment(scope, id));
end
return Ast.AssignmentStatement(idStats, vals);
end
function Compiler:copyRegisters(scope, to, from)
local idStats = {};
local vals = {};
for i, id in ipairs(to) do
local fromId = from[i];
if(fromId ~= id) then
table.insert(idStats, self:registerAssignment(scope, id));
table.insert(vals, self:register(scope, fromId));
end
end
if(#idStats > 0 and #vals > 0) then
return Ast.AssignmentStatement(idStats, vals);
end
end
function Compiler:resetRegisters()
self.registers = {};
end
function Compiler:pos(scope)
scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);
return Ast.VariableExpression(self.containerFuncScope, self.posVar);
end
function Compiler:posAssignment(scope)
scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);
return Ast.AssignmentVariable(self.containerFuncScope, self.posVar);
end
function Compiler:args(scope)
scope:addReferenceToHigherScope(self.containerFuncScope, self.argsVar);
return Ast.VariableExpression(self.containerFuncScope, self.argsVar);
end
function Compiler:unpack(scope)
scope:addReferenceToHigherScope(self.scope, self.unpackVar);
return Ast.VariableExpression(self.scope, self.unpackVar);
end
function Compiler:env(scope)
scope:addReferenceToHigherScope(self.scope, self.envVar);
return Ast.VariableExpression(self.scope, self.envVar);
end
function Compiler:jmp(scope, to)
scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);
return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.posVar)},{to});
end
function Compiler:setPos(scope, val)
if not val then
local v = Ast.IndexExpression(self:env(scope), randomStrings.randomStringNode(math.random(12, 14)));
scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);
return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.posVar)}, {v});
end
scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar);
return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.posVar)}, {Ast.NumberExpression(val) or Ast.NilExpression()});
end
function Compiler:setReturn(scope, val)
scope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar);
return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.returnVar)}, {val});
end
function Compiler:getReturn(scope)
scope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar);
return Ast.VariableExpression(self.containerFuncScope, self.returnVar);
end
function Compiler:returnAssignment(scope)
scope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar);
return Ast.AssignmentVariable(self.containerFuncScope, self.returnVar);
end
function Compiler:setUpvalueMember(scope, idExpr, valExpr, compoundConstructor)
scope:addReferenceToHigherScope(self.scope, self.upvaluesTable);
if compoundConstructor then
return compoundConstructor(Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), idExpr), valExpr);
end
return Ast.AssignmentStatement({Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), idExpr)}, {valExpr});
end
function Compiler:getUpvalueMember(scope, idExpr)
scope:addReferenceToHigherScope(self.scope, self.upvaluesTable);
return Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesTable), idExpr);
end
end
================================================
FILE: src/prometheus/compiler/statements/assignment.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- assignment.lua
--
-- This Script contains the statement handler for the AssignmentStatement.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local exprregs = {};
local assignmentIndexingRegs = {};
for i, primaryExpr in ipairs(statement.lhs) do
if(primaryExpr.kind == AstKind.AssignmentIndexing) then
assignmentIndexingRegs [i] = {
base = self:compileExpression(primaryExpr.base, funcDepth, 1)[1],
index = self:compileExpression(primaryExpr.index, funcDepth, 1)[1],
};
end
end
for i, expr in ipairs(statement.rhs) do
if(i == #statement.rhs and #statement.lhs > #statement.rhs) then
local regs = self:compileExpression(expr, funcDepth, #statement.lhs - #statement.rhs + 1);
for i, reg in ipairs(regs) do
if(self:isVarRegister(reg)) then
local ro = reg;
reg = self:allocRegister(false);
self:addStatement(self:copyRegisters(scope, {reg}, {ro}), {reg}, {ro}, false);
end
table.insert(exprregs, reg);
end
else
if statement.lhs[i] or expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression then
local reg = self:compileExpression(expr, funcDepth, 1)[1];
if(self:isVarRegister(reg)) then
local ro = reg;
reg = self:allocRegister(false);
self:addStatement(self:copyRegisters(scope, {reg}, {ro}), {reg}, {ro}, false);
end
table.insert(exprregs, reg);
end
end
end
for i, primaryExpr in ipairs(statement.lhs) do
if primaryExpr.kind == AstKind.AssignmentVariable then
if primaryExpr.scope.isGlobal then
local tmpReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(primaryExpr.scope:getVariableName(primaryExpr.id))), {tmpReg}, {}, false);
self:addStatement(Ast.AssignmentStatement({Ast.AssignmentIndexing(self:env(scope), self:register(scope, tmpReg))},
{self:register(scope, exprregs[i])}), {}, {tmpReg, exprregs[i]}, true);
self:freeRegister(tmpReg, false);
else
if self.scopeFunctionDepths[primaryExpr.scope] == funcDepth then
if self:isUpvalue(primaryExpr.scope, primaryExpr.id) then
local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, reg), self:register(scope, exprregs[i])), {}, {reg, exprregs[i]}, true);
else
local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth, exprregs[i]);
if reg ~= exprregs[i] then
self:addStatement(self:setRegister(scope, reg, self:register(scope, exprregs[i])), {reg}, {exprregs[i]}, false);
end
end
else
local upvalId = self:getUpvalueId(primaryExpr.scope, primaryExpr.id);
scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);
self:addStatement(self:setUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)), self:register(scope, exprregs[i])), {}, {exprregs[i]}, true);
end
end
elseif primaryExpr.kind == AstKind.AssignmentIndexing then
local baseReg = assignmentIndexingRegs[i].base;
local indexReg = assignmentIndexingRegs[i].index;
self:addStatement(Ast.AssignmentStatement({
Ast.AssignmentIndexing(self:register(scope, baseReg), self:register(scope, indexReg))
}, {
self:register(scope, exprregs[i])
}), {}, {exprregs[i], baseReg, indexReg}, true);
self:freeRegister(exprregs[i], false);
self:freeRegister(baseReg, false);
self:freeRegister(indexReg, false);
else
error(string.format("Invalid Assignment lhs: %s", statement.lhs));
end
end
end;
================================================
FILE: src/prometheus/compiler/statements/break_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- break_statement.lua
--
-- This Script contains the statement handler for the BreakStatement.
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local toFreeVars = {};
local statScope;
repeat
statScope = statScope and statScope.parentScope or statement.scope;
for id, name in ipairs(statScope.variables) do
table.insert(toFreeVars, {
scope = statScope,
id = id;
});
end
until statScope == statement.loop.body.scope;
for i, var in pairs(toFreeVars) do
local varScope, id = var.scope, var.id;
local varReg = self:getVarRegister(varScope, id, nil, nil);
if self:isUpvalue(varScope, id) then
scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);
self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), {
self:register(scope, varReg)
})), {varReg}, {varReg}, false);
else
self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), {varReg}, {}, false);
end
end
self:addStatement(self:setPos(scope, statement.loop.__final_block.id), {self.POS_REGISTER}, {}, false);
self.activeBlock.advanceToNextBlock = false;
end;
================================================
FILE: src/prometheus/compiler/statements/compound.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- compound.lua
--
-- This Script contains the statement handler for the Compound statements (compound add, sub, mul, etc.)
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
local compoundConstructors = {
[AstKind.CompoundAddStatement] = Ast.CompoundAddStatement,
[AstKind.CompoundSubStatement] = Ast.CompoundSubStatement,
[AstKind.CompoundMulStatement] = Ast.CompoundMulStatement,
[AstKind.CompoundDivStatement] = Ast.CompoundDivStatement,
[AstKind.CompoundModStatement] = Ast.CompoundModStatement,
[AstKind.CompoundPowStatement] = Ast.CompoundPowStatement,
[AstKind.CompoundConcatStatement] = Ast.CompoundConcatStatement,
};
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local compoundConstructor = compoundConstructors[statement.kind];
if statement.lhs.kind == AstKind.AssignmentIndexing then
local indexing = statement.lhs;
local baseReg = self:compileExpression(indexing.base, funcDepth, 1)[1];
local indexReg = self:compileExpression(indexing.index, funcDepth, 1)[1];
local valueReg = self:compileExpression(statement.rhs, funcDepth, 1)[1];
self:addStatement(compoundConstructor(Ast.AssignmentIndexing(self:register(scope, baseReg), self:register(scope, indexReg)), self:register(scope, valueReg)), {}, {baseReg, indexReg, valueReg}, true);
self:freeRegister(baseReg, false);
self:freeRegister(indexReg, false);
self:freeRegister(valueReg, false);
else
local valueReg = self:compileExpression(statement.rhs, funcDepth, 1)[1];
local primaryExpr = statement.lhs;
if primaryExpr.scope.isGlobal then
local tmpReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(primaryExpr.scope:getVariableName(primaryExpr.id))), {tmpReg}, {}, false);
self:addStatement(compoundConstructor(Ast.AssignmentIndexing(self:env(scope), self:register(scope, tmpReg)),
self:register(scope, valueReg)), {}, {tmpReg, valueReg}, true);
self:freeRegister(tmpReg, false);
self:freeRegister(valueReg, false);
else
if self.scopeFunctionDepths[primaryExpr.scope] == funcDepth then
if self:isUpvalue(primaryExpr.scope, primaryExpr.id) then
local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, reg), self:register(scope, valueReg), compoundConstructor), {}, {reg, valueReg}, true);
else
local reg = self:getVarRegister(primaryExpr.scope, primaryExpr.id, funcDepth, valueReg);
if reg ~= valueReg then
self:addStatement(self:setRegister(scope, reg, self:register(scope, valueReg), compoundConstructor), {reg}, {valueReg}, false);
end
end
else
local upvalId = self:getUpvalueId(primaryExpr.scope, primaryExpr.id);
scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);
self:addStatement(self:setUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)), self:register(scope, valueReg), compoundConstructor), {}, {valueReg}, true);
end
self:freeRegister(valueReg, false);
end
end
end;
================================================
FILE: src/prometheus/compiler/statements/continue_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- continue_statement.lua
--
-- This Script contains the statement handler for the ContinueStatement.
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local toFreeVars = {};
local statScope;
repeat
statScope = statScope and statScope.parentScope or statement.scope;
for id, _ in pairs(statScope.variables) do
table.insert(toFreeVars, {
scope = statScope,
id = id;
});
end
until statScope == statement.loop.body.scope;
for _, var in ipairs(toFreeVars) do
local varScope, id = var.scope, var.id;
local varReg = self:getVarRegister(varScope, id, nil, nil);
if self:isUpvalue(varScope, id) then
scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);
self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), {
self:register(scope, varReg)
})), {varReg}, {varReg}, false);
else
self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), {varReg}, {}, false);
end
end
self:addStatement(self:setPos(scope, statement.loop.__start_block.id), {self.POS_REGISTER}, {}, false);
self.activeBlock.advanceToNextBlock = false;
end;
================================================
FILE: src/prometheus/compiler/statements/do_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- do_statement.lua
--
-- This Script contains the statement handler for the DoStatement.
return function(self, statement, funcDepth)
self:compileBlock(statement.body, funcDepth);
end;
================================================
FILE: src/prometheus/compiler/statements/for_in_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- for_in_statement.lua
--
-- This Script contains the statement handler for the ForInStatement
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local expressionsLength = #statement.expressions;
local exprregs = {};
for i, expr in ipairs(statement.expressions) do
if(i == expressionsLength and expressionsLength < 3) then
local regs = self:compileExpression(expr, funcDepth, 4 - expressionsLength);
for i = 1, 4 - expressionsLength do
table.insert(exprregs, regs[i]);
end
else
if i <= 3 then
table.insert(exprregs, self:compileExpression(expr, funcDepth, 1)[1])
else
self:freeRegister(self:compileExpression(expr, funcDepth, 1)[1], false);
end
end
end
for i, reg in ipairs(exprregs) do
if reg and self.registers[reg] ~= self.VAR_REGISTER and reg ~= self.POS_REGISTER and reg ~= self.RETURN_REGISTER then
self.registers[reg] = self.VAR_REGISTER;
else
exprregs[i] = self:allocRegister(true);
self:addStatement(self:copyRegisters(scope, {exprregs[i]}, {reg}), {exprregs[i]}, {reg}, false);
end
end
local checkBlock = self:createBlock();
local bodyBlock = self:createBlock();
local finalBlock = self:createBlock();
statement.__start_block = checkBlock;
statement.__final_block = finalBlock;
self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);
self:setActiveBlock(checkBlock);
local scope = self.activeBlock.scope;
local varRegs = {};
for i, id in ipairs(statement.ids) do
varRegs[i] = self:getVarRegister(statement.scope, id, funcDepth)
end
self:addStatement(Ast.AssignmentStatement({
self:registerAssignment(scope, exprregs[3]),
varRegs[2] and self:registerAssignment(scope, varRegs[2]),
}, {
Ast.FunctionCallExpression(self:register(scope, exprregs[1]), {
self:register(scope, exprregs[2]),
self:register(scope, exprregs[3]),
})
}), {exprregs[3], varRegs[2]}, {exprregs[1], exprregs[2], exprregs[3]}, true);
self:addStatement(Ast.AssignmentStatement({
self:posAssignment(scope)
}, {
Ast.OrExpression(Ast.AndExpression(self:register(scope, exprregs[3]), Ast.NumberExpression(bodyBlock.id)), Ast.NumberExpression(finalBlock.id))
}), {self.POS_REGISTER}, {exprregs[3]}, false);
self:setActiveBlock(bodyBlock);
local scope = self.activeBlock.scope;
self:addStatement(self:copyRegisters(scope, {varRegs[1]}, {exprregs[3]}), {varRegs[1]}, {exprregs[3]}, false);
for i=3, #varRegs do
self:addStatement(self:setRegister(scope, varRegs[i], Ast.NilExpression()), {varRegs[i]}, {}, false);
end
for i, id in ipairs(statement.ids) do
if(self:isUpvalue(statement.scope, id)) then
local varreg = varRegs[i];
local tmpReg = self:allocRegister(false);
scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);
self:addStatement(self:setRegister(scope, tmpReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {tmpReg}, {}, false);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, tmpReg), self:register(scope, varreg)), {}, {tmpReg, varreg}, true);
self:addStatement(self:copyRegisters(scope, {varreg}, {tmpReg}), {varreg}, {tmpReg}, false);
self:freeRegister(tmpReg, false);
end
end
self:compileBlock(statement.body, funcDepth);
self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);
self:setActiveBlock(finalBlock);
for i, _ in ipairs(exprregs) do
self:freeRegister(exprregs[i], true)
end
end;
================================================
FILE: src/prometheus/compiler/statements/for_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- for_statement.lua
--
-- This Script contains the statement handler for the ForStatement
local Ast = require("prometheus.ast");
local util = require("prometheus.util");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local checkBlock = self:createBlock();
local innerBlock = self:createBlock();
local finalBlock = self:createBlock();
statement.__start_block = checkBlock;
statement.__final_block = finalBlock;
local posState = self.registers[self.POS_REGISTER];
self.registers[self.POS_REGISTER] = self.VAR_REGISTER;
local initialReg = self:compileExpression(statement.initialValue, funcDepth, 1)[1];
local finalExprReg = self:compileExpression(statement.finalValue, funcDepth, 1)[1];
local finalReg = self:allocRegister(false);
self:addStatement(self:copyRegisters(scope, {finalReg}, {finalExprReg}), {finalReg}, {finalExprReg}, false);
self:freeRegister(finalExprReg);
local incrementExprReg = self:compileExpression(statement.incrementBy, funcDepth, 1)[1];
local incrementReg = self:allocRegister(false);
self:addStatement(self:copyRegisters(scope, {incrementReg}, {incrementExprReg}), {incrementReg}, {incrementExprReg}, false);
self:freeRegister(incrementExprReg);
local tmpReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.NumberExpression(0)), {tmpReg}, {}, false);
local incrementIsNegReg = self:allocRegister(false);
local shouldSwap3 = math.random(1, 2) == 2;
local shuffledRegs4 = shouldSwap3 and {incrementReg, tmpReg} or {tmpReg, incrementReg};
self:addStatement(self:setRegister(scope, incrementIsNegReg, Ast[shouldSwap3 and "LessThanExpression" or "GreaterThanExpression"](self:register(scope, shuffledRegs4[1]), self:register(scope, shuffledRegs4[2]))), {incrementIsNegReg}, {shuffledRegs4[1], shuffledRegs4[2]}, false);
self:freeRegister(tmpReg);
local currentReg = self:allocRegister(true);
self:addStatement(self:setRegister(scope, currentReg, Ast.SubExpression(self:register(scope, initialReg), self:register(scope, incrementReg))), {currentReg}, {initialReg, incrementReg}, false);
self:freeRegister(initialReg);
self:addStatement(self:jmp(scope, Ast.NumberExpression(checkBlock.id)), {self.POS_REGISTER}, {}, false);
self:setActiveBlock(checkBlock);
scope = checkBlock.scope;
-- x = x + y or x = y + x instead of just x = x + y.
--> NEW: In an attempt to thwart deobfuscations, despite this being a simple comparison... Shuffling these causes problems for decompilers.
--> NOTE: This isn't unstable code, I've tested it multiple times.
local shuffledRegs = util.shuffle({currentReg, incrementReg});
self:addStatement(self:setRegister(scope, currentReg, Ast.AddExpression(self:register(scope, shuffledRegs[1]), self:register(scope, shuffledRegs[2]))), {currentReg}, {shuffledRegs[1], shuffledRegs[2]}, false);
local tmpReg1 = self:allocRegister(false);
local tmpReg2 = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg2, Ast.NotExpression(self:register(scope, incrementIsNegReg))), {tmpReg2}, {incrementIsNegReg}, false);
local shouldSwap = math.random(1, 2) == 2;
local shuffledRegs2 = shouldSwap and {currentReg, finalReg} or {finalReg, currentReg};
self:addStatement(self:setRegister(scope, tmpReg1, Ast[shouldSwap and "LessThanOrEqualsExpression" or "GreaterThanOrEqualsExpression"](self:register(scope, shuffledRegs2[1]), self:register(scope, shuffledRegs2[2]))), {tmpReg1}, {shuffledRegs2[1], shuffledRegs2[2]}, false);
self:addStatement(self:setRegister(scope, tmpReg1, Ast.AndExpression(self:register(scope, tmpReg2), self:register(scope, tmpReg1))), {tmpReg1}, {tmpReg1, tmpReg2}, false);
local shouldSwap2 = math.random(1, 2) == 2;
local shuffledRegs3 = shouldSwap2 and {currentReg, finalReg} or {finalReg, currentReg};
self:addStatement(self:setRegister(scope, tmpReg2, Ast[shouldSwap2 and "GreaterThanOrEqualsExpression" or "LessThanOrEqualsExpression"](self:register(scope, shuffledRegs3[1]), self:register(scope, shuffledRegs3[2]))), {tmpReg2}, {shuffledRegs3[1], shuffledRegs3[2]}, false);
self:addStatement(self:setRegister(scope, tmpReg2, Ast.AndExpression(self:register(scope, incrementIsNegReg), self:register(scope, tmpReg2))), {tmpReg2}, {tmpReg2, incrementIsNegReg}, false);
self:addStatement(self:setRegister(scope, tmpReg1, Ast.OrExpression(self:register(scope, tmpReg2), self:register(scope, tmpReg1))), {tmpReg1}, {tmpReg1, tmpReg2}, false);
self:freeRegister(tmpReg2);
tmpReg2 = self:compileExpression(Ast.NumberExpression(innerBlock.id), funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.AndExpression(self:register(scope, tmpReg1), self:register(scope, tmpReg2))), {self.POS_REGISTER}, {tmpReg1, tmpReg2}, false);
self:freeRegister(tmpReg2);
self:freeRegister(tmpReg1);
tmpReg2 = self:compileExpression(Ast.NumberExpression(finalBlock.id), funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(self:register(scope, self.POS_REGISTER), self:register(scope, tmpReg2))), {self.POS_REGISTER}, {self.POS_REGISTER, tmpReg2}, false);
self:freeRegister(tmpReg2);
self:setActiveBlock(innerBlock);
scope = innerBlock.scope;
self.registers[self.POS_REGISTER] = posState;
local varReg = self:getVarRegister(statement.scope, statement.id, funcDepth, nil);
if(self:isUpvalue(statement.scope, statement.id)) then
scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);
self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {varReg}, {}, false);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, varReg), self:register(scope, currentReg)), {}, {varReg, currentReg}, true);
else
self:addStatement(self:setRegister(scope, varReg, self:register(scope, currentReg)), {varReg}, {currentReg}, false);
end
self:compileBlock(statement.body, funcDepth);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(checkBlock.id)), {self.POS_REGISTER}, {}, false);
self.registers[self.POS_REGISTER] = self.VAR_REGISTER;
self:freeRegister(finalReg);
self:freeRegister(incrementIsNegReg);
self:freeRegister(incrementReg);
self:freeRegister(currentReg, true);
self.registers[self.POS_REGISTER] = posState;
self:setActiveBlock(finalBlock);
end;
================================================
FILE: src/prometheus/compiler/statements/function_call.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- function_call.lua
--
-- This Script contains the statement handler for the FunctionCallStatement.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local baseReg = self:compileExpression(statement.base, funcDepth, 1)[1];
local retReg = self:allocRegister(false);
local regs = {};
local args = {};
for i, expr in ipairs(statement.args) do
if i == #statement.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then
local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];
table.insert(args, Ast.FunctionCallExpression(
self:unpack(scope),
{self:register(scope, reg)}));
table.insert(regs, reg);
else
local reg = self:compileExpression(expr, funcDepth, 1)[1];
local argExpr = self:register(scope, reg);
table.insert(args, argExpr);
table.insert(regs, reg);
end
end
self:addStatement(self:setRegister(scope, retReg, Ast.FunctionCallExpression(self:register(scope, baseReg), args)), {retReg}, {baseReg, unpack(regs)}, true);
self:freeRegister(baseReg, false);
self:freeRegister(retReg, false);
for _, reg in ipairs(regs) do
self:freeRegister(reg, false);
end
end;
================================================
FILE: src/prometheus/compiler/statements/function_declaration.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- function_declaration.lua
--
-- This Script contains the statement handler for the FunctionDeclaration.
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local retReg = self:compileFunction(statement, funcDepth);
if(#statement.indices > 0) then
local tblReg;
if statement.scope.isGlobal then
tblReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tblReg, Ast.StringExpression(statement.scope:getVariableName(statement.id))), {tblReg}, {}, false);
self:addStatement(self:setRegister(scope, tblReg, Ast.IndexExpression(self:env(scope), self:register(scope, tblReg))), {tblReg}, {tblReg}, true);
else
if self.scopeFunctionDepths[statement.scope] == funcDepth then
if self:isUpvalue(statement.scope, statement.id) then
tblReg = self:allocRegister(false);
local reg = self:getVarRegister(statement.scope, statement.id, funcDepth);
self:addStatement(self:setRegister(scope, tblReg, self:getUpvalueMember(scope, self:register(scope, reg))), {tblReg}, {reg}, true);
else
tblReg = self:getVarRegister(statement.scope, statement.id, funcDepth, retReg);
end
else
tblReg = self:allocRegister(false);
local upvalId = self:getUpvalueId(statement.scope, statement.id);
scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);
self:addStatement(self:setRegister(scope, tblReg, self:getUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)))), {tblReg}, {}, true);
end
end
for i = 1, #statement.indices - 1 do
local index = statement.indices[i];
local indexReg = self:compileExpression(Ast.StringExpression(index), funcDepth, 1)[1];
local tblRegOld = tblReg;
tblReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tblReg, Ast.IndexExpression(self:register(scope, tblRegOld), self:register(scope, indexReg))), {tblReg}, {tblReg, indexReg}, false);
self:freeRegister(tblRegOld, false);
self:freeRegister(indexReg, false);
end
local index = statement.indices[#statement.indices];
local indexReg = self:compileExpression(Ast.StringExpression(index), funcDepth, 1)[1];
self:addStatement(Ast.AssignmentStatement({
Ast.AssignmentIndexing(self:register(scope, tblReg), self:register(scope, indexReg)),
}, {
self:register(scope, retReg),
}), {}, {tblReg, indexReg, retReg}, true);
self:freeRegister(indexReg, false);
self:freeRegister(tblReg, false);
self:freeRegister(retReg, false);
return;
end
if statement.scope.isGlobal then
local tmpReg = self:allocRegister(false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(statement.scope:getVariableName(statement.id))), {tmpReg}, {}, false);
self:addStatement(Ast.AssignmentStatement({Ast.AssignmentIndexing(self:env(scope), self:register(scope, tmpReg))},
{self:register(scope, retReg)}), {}, {tmpReg, retReg}, true);
self:freeRegister(tmpReg, false);
else
if self.scopeFunctionDepths[statement.scope] == funcDepth then
if self:isUpvalue(statement.scope, statement.id) then
local reg = self:getVarRegister(statement.scope, statement.id, funcDepth);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, reg), self:register(scope, retReg)), {}, {reg, retReg}, true);
else
local reg = self:getVarRegister(statement.scope, statement.id, funcDepth, retReg);
if reg ~= retReg then
self:addStatement(self:setRegister(scope, reg, self:register(scope, retReg)), {reg}, {retReg}, false);
end
end
else
local upvalId = self:getUpvalueId(statement.scope, statement.id);
scope:addReferenceToHigherScope(self.containerFuncScope, self.currentUpvaluesVar);
self:addStatement(self:setUpvalueMember(scope, Ast.IndexExpression(Ast.VariableExpression(self.containerFuncScope, self.currentUpvaluesVar), Ast.NumberExpression(upvalId)), self:register(scope, retReg)), {}, {retReg}, true);
end
end
self:freeRegister(retReg, false);
end;
================================================
FILE: src/prometheus/compiler/statements/if_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- if_statement.lua
--
-- This Script contains the statement handler for the IfStatement.
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local conditionReg = self:compileExpression(statement.condition, funcDepth, 1)[1];
local finalBlock = self:createBlock();
local nextBlock
if statement.elsebody or #statement.elseifs > 0 then
nextBlock = self:createBlock();
else
nextBlock = finalBlock;
end
local innerBlock = self:createBlock();
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(nextBlock.id))), {self.POS_REGISTER}, {conditionReg}, false);
self:freeRegister(conditionReg, false);
self:setActiveBlock(innerBlock);
scope = innerBlock.scope
self:compileBlock(statement.body, funcDepth);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(finalBlock.id)), {self.POS_REGISTER}, {}, false);
for i, eif in ipairs(statement.elseifs) do
self:setActiveBlock(nextBlock);
conditionReg = self:compileExpression(eif.condition, funcDepth, 1)[1];
local innerBlock = self:createBlock();
if statement.elsebody or i < #statement.elseifs then
nextBlock = self:createBlock();
else
nextBlock = finalBlock;
end
local scope = self.activeBlock.scope;
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(nextBlock.id))), {self.POS_REGISTER}, {conditionReg}, false);
self:freeRegister(conditionReg, false);
self:setActiveBlock(innerBlock);
scope = innerBlock.scope;
self:compileBlock(eif.body, funcDepth);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(finalBlock.id)), {self.POS_REGISTER}, {}, false);
end
if statement.elsebody then
self:setActiveBlock(nextBlock);
self:compileBlock(statement.elsebody, funcDepth);
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(finalBlock.id)), {self.POS_REGISTER}, {}, false);
end
self:setActiveBlock(finalBlock);
end;
================================================
FILE: src/prometheus/compiler/statements/local_function_declaration.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- local_function_declaration.lua
--
-- This Script contains the statement handler for the LocalFunctionDeclaration
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
if(self:isUpvalue(statement.scope, statement.id)) then
local varReg = self:getVarRegister(statement.scope, statement.id, funcDepth, nil);
scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);
self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {varReg}, {}, false);
local retReg = self:compileFunction(statement, funcDepth);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, varReg), self:register(scope, retReg)), {}, {varReg, retReg}, true);
self:freeRegister(retReg, false);
else
local retReg = self:compileFunction(statement, funcDepth);
local varReg = self:getVarRegister(statement.scope, statement.id, funcDepth, retReg);
self:addStatement(self:copyRegisters(scope, {varReg}, {retReg}), {varReg}, {retReg}, false);
self:freeRegister(retReg, false);
end
end;
================================================
FILE: src/prometheus/compiler/statements/local_variable_declaration.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- local_variable_declaration.lua
--
-- This Script contains the statement handler for the LocalVariableDeclaration
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local exprregs = {};
for i, expr in ipairs(statement.expressions) do
if(i == #statement.expressions and #statement.ids > #statement.expressions) then
local regs = self:compileExpression(expr, funcDepth, #statement.ids - #statement.expressions + 1);
for i, reg in ipairs(regs) do
table.insert(exprregs, reg);
end
else
if statement.ids[i] or expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression then
local reg = self:compileExpression(expr, funcDepth, 1)[1];
table.insert(exprregs, reg);
end
end
end
if #exprregs == 0 then
for _=1, #statement.ids do
table.insert(exprregs, self:compileExpression(Ast.NilExpression(), funcDepth, 1)[1]);
end
end
for i, id in ipairs(statement.ids) do
if(exprregs[i]) then
if(self:isUpvalue(statement.scope, id)) then
local varreg = self:getVarRegister(statement.scope, id, funcDepth);
local varReg = self:getVarRegister(statement.scope, id, funcDepth, nil);
scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction);
self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.allocUpvalFunction), {})), {varReg}, {}, false);
self:addStatement(self:setUpvalueMember(scope, self:register(scope, varReg), self:register(scope, exprregs[i])), {}, {varReg, exprregs[i]}, true);
self:freeRegister(exprregs[i], false);
else
local varreg = self:getVarRegister(statement.scope, id, funcDepth, exprregs[i]);
self:addStatement(self:copyRegisters(scope, {varreg}, {exprregs[i]}), {varreg}, {exprregs[i]}, false);
self:freeRegister(exprregs[i], false);
end
end
end
if not self.scopeFunctionDepths[statement.scope] then
self.scopeFunctionDepths[statement.scope] = funcDepth;
end
end;
================================================
FILE: src/prometheus/compiler/statements/pass_self_function_call.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- pass_self_function_call.lua
--
-- This Script contains the statement handler for the PassSelfFunctionCallStatement.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local baseReg = self:compileExpression(statement.base, funcDepth, 1)[1];
local tmpReg = self:allocRegister(false);
local args = { self:register(scope, baseReg) };
local regs = { baseReg };
for i, expr in ipairs(statement.args) do
if i == #statement.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then
local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];
table.insert(args, Ast.FunctionCallExpression(
self:unpack(scope),
{self:register(scope, reg)}));
table.insert(regs, reg);
else
local reg = self:compileExpression(expr, funcDepth, 1)[1];
table.insert(args, self:register(scope, reg));
table.insert(regs, reg);
end
end
self:addStatement(self:setRegister(scope, tmpReg, Ast.StringExpression(statement.passSelfFunctionName)), {tmpReg}, {}, false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.IndexExpression(self:register(scope, baseReg), self:register(scope, tmpReg))), {tmpReg}, {tmpReg, baseReg}, false);
self:addStatement(self:setRegister(scope, tmpReg, Ast.FunctionCallExpression(self:register(scope, tmpReg), args)), {tmpReg}, {tmpReg, unpack(regs)}, true);
self:freeRegister(tmpReg, false);
for _, reg in ipairs(regs) do
self:freeRegister(reg, false);
end
end;
================================================
FILE: src/prometheus/compiler/statements/repeat_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- repeat_statement.lua
--
-- This Script contains the statement handler for the RepeatStatement.
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local innerBlock = self:createBlock();
local finalBlock = self:createBlock();
statement.__start_block = innerBlock;
statement.__final_block = finalBlock;
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(innerBlock.id)), {self.POS_REGISTER}, {}, false);
self:setActiveBlock(innerBlock);
for _, stat in ipairs(statement.body.statements) do
self:compileStatement(stat, funcDepth);
end;
local scope = self.activeBlock.scope;
local conditionReg = (self:compileExpression(statement.condition, funcDepth, 1))[1];
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(finalBlock.id)), Ast.NumberExpression(innerBlock.id))), { self.POS_REGISTER }, { conditionReg }, false);
self:freeRegister(conditionReg, false);
for id, _ in ipairs(statement.body.scope.variables) do
local varReg = self:getVarRegister(statement.body.scope, id, funcDepth, nil);
if self:isUpvalue(statement.body.scope, id) then
scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc);
self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), { self:register(scope, varReg) })), { varReg }, { varReg }, false);
else
self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), { varReg }, {}, false);
end;
self:freeRegister(varReg, true);
end;
self:setActiveBlock(finalBlock);
end;
================================================
FILE: src/prometheus/compiler/statements/return.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- return.lua
--
-- This Script contains the statement handler for the ReturnStatement.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local entries = {};
local regs = {};
for i, expr in ipairs(statement.args) do
if i == #statement.args and (expr.kind == AstKind.FunctionCallExpression or expr.kind == AstKind.PassSelfFunctionCallExpression or expr.kind == AstKind.VarargExpression) then
local reg = self:compileExpression(expr, funcDepth, self.RETURN_ALL)[1];
table.insert(entries, Ast.TableEntry(Ast.FunctionCallExpression(
self:unpack(scope),
{self:register(scope, reg)})));
table.insert(regs, reg);
else
local reg = self:compileExpression(expr, funcDepth, 1)[1];
table.insert(entries, Ast.TableEntry(self:register(scope, reg)));
table.insert(regs, reg);
end
end
for _, reg in ipairs(regs) do
self:freeRegister(reg, false);
end
self:addStatement(self:setReturn(scope, Ast.TableConstructorExpression(entries)), {self.RETURN_REGISTER}, regs, false);
self:addStatement(self:setPos(self.activeBlock.scope, nil), {self.POS_REGISTER}, {}, false);
self.activeBlock.advanceToNextBlock = false;
end;
================================================
FILE: src/prometheus/compiler/statements/while_statement.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- while_statement.lua
--
-- This Script contains the statement handler for the WhileStatement
local Ast = require("prometheus.ast");
return function(self, statement, funcDepth)
local scope = self.activeBlock.scope;
local innerBlock = self:createBlock();
local finalBlock = self:createBlock();
local checkBlock = self:createBlock();
statement.__start_block = checkBlock;
statement.__final_block = finalBlock;
self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);
self:setActiveBlock(checkBlock);
scope = self.activeBlock.scope;
local conditionReg = self:compileExpression(statement.condition, funcDepth, 1)[1];
self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(finalBlock.id))), {self.POS_REGISTER}, {conditionReg}, false);
self:freeRegister(conditionReg, false);
self:setActiveBlock(innerBlock);
local scope = self.activeBlock.scope;
self:compileBlock(statement.body, funcDepth);
self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false);
self:setActiveBlock(finalBlock);
end;
================================================
FILE: src/prometheus/compiler/statements.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- statements.lua
--
-- This Script contains the statement handlers: exports handler table keyed by AstKind.
local Ast = require("prometheus.ast");
local AstKind = Ast.AstKind;
local handlers = {};
local statements = "prometheus.compiler.statements.";
local function requireStatement(name)
return require(statements .. name);
end
handlers[AstKind.ReturnStatement] = requireStatement("return");
handlers[AstKind.LocalVariableDeclaration] = requireStatement("local_variable_declaration");
handlers[AstKind.FunctionCallStatement] = requireStatement("function_call");
handlers[AstKind.PassSelfFunctionCallStatement] = requireStatement("pass_self_function_call");
handlers[AstKind.LocalFunctionDeclaration] = requireStatement("local_function_declaration");
handlers[AstKind.FunctionDeclaration] = requireStatement("function_declaration");
handlers[AstKind.AssignmentStatement] = requireStatement("assignment");
handlers[AstKind.IfStatement] = requireStatement("if_statement");
handlers[AstKind.DoStatement] = requireStatement("do_statement");
handlers[AstKind.WhileStatement] = requireStatement("while_statement");
handlers[AstKind.RepeatStatement] = requireStatement("repeat_statement");
handlers[AstKind.ForStatement] = requireStatement("for_statement");
handlers[AstKind.ForInStatement] = requireStatement("for_in_statement");
handlers[AstKind.BreakStatement] = requireStatement("break_statement");
handlers[AstKind.ContinueStatement] = requireStatement("continue_statement");
-- Compound statements share one handler
local compoundHandler = requireStatement("compound");
handlers[AstKind.CompoundAddStatement] = compoundHandler;
handlers[AstKind.CompoundSubStatement] = compoundHandler;
handlers[AstKind.CompoundMulStatement] = compoundHandler;
handlers[AstKind.CompoundDivStatement] = compoundHandler;
handlers[AstKind.CompoundModStatement] = compoundHandler;
handlers[AstKind.CompoundPowStatement] = compoundHandler;
handlers[AstKind.CompoundConcatStatement] = compoundHandler;
return handlers;
================================================
FILE: src/prometheus/compiler/upvalue.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- upvalue.lua
--
-- This Script contains the upvalue and GC management for the compiler.
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local util = require("prometheus.util");
local unpack = unpack or table.unpack;
return function(Compiler)
function Compiler:createUpvaluesGcFunc()
local scope = Scope:new(self.scope);
local selfVar = scope:addVariable();
local iteratorVar = scope:addVariable();
local valueVar = scope:addVariable();
local whileScope = Scope:new(scope);
whileScope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 3);
whileScope:addReferenceToHigherScope(scope, valueVar, 3);
whileScope:addReferenceToHigherScope(scope, iteratorVar, 3);
local ifScope = Scope:new(whileScope);
ifScope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 1);
ifScope:addReferenceToHigherScope(self.scope, self.upvaluesTable, 1);
return Ast.FunctionLiteralExpression({Ast.VariableExpression(scope, selfVar)}, Ast.Block({
Ast.LocalVariableDeclaration(scope, {iteratorVar, valueVar}, {Ast.NumberExpression(1), Ast.IndexExpression(Ast.VariableExpression(scope, selfVar), Ast.NumberExpression(1))}),
Ast.WhileStatement(Ast.Block({
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)),
Ast.AssignmentVariable(scope, iteratorVar),
}, {
Ast.SubExpression(Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)), Ast.NumberExpression(1)),
Ast.AddExpression(unpack(util.shuffle{Ast.VariableExpression(scope, iteratorVar), Ast.NumberExpression(1)})),
}),
Ast.IfStatement(Ast.EqualsExpression(unpack(util.shuffle{Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)), Ast.NumberExpression(0)})), Ast.Block({
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, valueVar)),
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), Ast.VariableExpression(scope, valueVar)),
}, {
Ast.NilExpression(),
Ast.NilExpression(),
})
}, ifScope), {}, nil),
Ast.AssignmentStatement({
Ast.AssignmentVariable(scope, valueVar),
}, {
Ast.IndexExpression(Ast.VariableExpression(scope, selfVar), Ast.VariableExpression(scope, iteratorVar)),
}),
}, whileScope), Ast.VariableExpression(scope, valueVar), scope);
}, scope));
end
function Compiler:createFreeUpvalueFunc()
local scope = Scope:new(self.scope);
local argVar = scope:addVariable();
local ifScope = Scope:new(scope);
ifScope:addReferenceToHigherScope(scope, argVar, 3);
scope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 2);
return Ast.FunctionLiteralExpression({Ast.VariableExpression(scope, argVar)}, Ast.Block({
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar))
}, {
Ast.SubExpression(Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar)), Ast.NumberExpression(1));
}),
Ast.IfStatement(Ast.EqualsExpression(unpack(util.shuffle{Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar)), Ast.NumberExpression(0)})), Ast.Block({
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(scope, argVar)),
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesTable), Ast.VariableExpression(scope, argVar)),
}, {
Ast.NilExpression(),
Ast.NilExpression(),
})
}, ifScope), {}, nil)
}, scope))
end
function Compiler:createUpvaluesProxyFunc()
local scope = Scope:new(self.scope);
scope:addReferenceToHigherScope(self.scope, self.newproxyVar);
local entriesVar = scope:addVariable();
local ifScope = Scope:new(scope);
local proxyVar = ifScope:addVariable();
local metatableVar = ifScope:addVariable();
local elseScope = Scope:new(scope);
ifScope:addReferenceToHigherScope(self.scope, self.newproxyVar);
ifScope:addReferenceToHigherScope(self.scope, self.getmetatableVar);
ifScope:addReferenceToHigherScope(self.scope, self.upvaluesGcFunctionVar);
ifScope:addReferenceToHigherScope(scope, entriesVar);
elseScope:addReferenceToHigherScope(self.scope, self.setmetatableVar);
elseScope:addReferenceToHigherScope(scope, entriesVar);
elseScope:addReferenceToHigherScope(self.scope, self.upvaluesGcFunctionVar);
local forScope = Scope:new(scope);
local forArg = forScope:addVariable();
forScope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 2);
forScope:addReferenceToHigherScope(scope, entriesVar, 2);
return Ast.FunctionLiteralExpression({Ast.VariableExpression(scope, entriesVar)}, Ast.Block({
Ast.ForStatement(forScope, forArg, Ast.NumberExpression(1), Ast.LenExpression(Ast.VariableExpression(scope, entriesVar)), Ast.NumberExpression(1), Ast.Block({
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.IndexExpression(Ast.VariableExpression(scope, entriesVar), Ast.VariableExpression(forScope, forArg)))
}, {
Ast.AddExpression(unpack(util.shuffle{
Ast.IndexExpression(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.IndexExpression(Ast.VariableExpression(scope, entriesVar), Ast.VariableExpression(forScope, forArg))),
Ast.NumberExpression(1),
}))
})
}, forScope), scope);
Ast.IfStatement(Ast.VariableExpression(self.scope, self.newproxyVar), Ast.Block({
Ast.LocalVariableDeclaration(ifScope, {proxyVar}, {
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.newproxyVar), {
Ast.BooleanExpression(true)
});
});
Ast.LocalVariableDeclaration(ifScope, {metatableVar}, {
Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.getmetatableVar), {
Ast.VariableExpression(ifScope, proxyVar);
});
});
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(ifScope, metatableVar), Ast.StringExpression("__index")),
Ast.AssignmentIndexing(Ast.VariableExpression(ifScope, metatableVar), Ast.StringExpression("__gc")),
Ast.AssignmentIndexing(Ast.VariableExpression(ifScope, metatableVar), Ast.StringExpression("__len")),
}, {
Ast.VariableExpression(scope, entriesVar),
Ast.VariableExpression(self.scope, self.upvaluesGcFunctionVar),
Ast.FunctionLiteralExpression({}, Ast.Block({
Ast.ReturnStatement({Ast.NumberExpression(self.upvalsProxyLenReturn)})
}, Scope:new(ifScope)));
});
Ast.ReturnStatement({
Ast.VariableExpression(ifScope, proxyVar)
})
}, ifScope), {}, Ast.Block({
Ast.ReturnStatement({Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.setmetatableVar), {
Ast.TableConstructorExpression({}),
Ast.TableConstructorExpression({
Ast.KeyedTableEntry(Ast.StringExpression("__gc"), Ast.VariableExpression(self.scope, self.upvaluesGcFunctionVar)),
Ast.KeyedTableEntry(Ast.StringExpression("__index"), Ast.VariableExpression(scope, entriesVar)),
Ast.KeyedTableEntry(Ast.StringExpression("__len"), Ast.FunctionLiteralExpression({}, Ast.Block({
Ast.ReturnStatement({Ast.NumberExpression(self.upvalsProxyLenReturn)})
}, Scope:new(ifScope)))),
})
})})
}, elseScope));
}, scope));
end
function Compiler:createAllocUpvalFunction()
local scope = Scope:new(self.scope);
scope:addReferenceToHigherScope(self.scope, self.currentUpvalId, 4);
scope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 1);
return Ast.FunctionLiteralExpression({}, Ast.Block({
Ast.AssignmentStatement({
Ast.AssignmentVariable(self.scope, self.currentUpvalId),
},{
Ast.AddExpression(unpack(util.shuffle({
Ast.VariableExpression(self.scope, self.currentUpvalId),
Ast.NumberExpression(1),
}))),
}
),
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(self.scope, self.upvaluesReferenceCountsTable), Ast.VariableExpression(self.scope, self.currentUpvalId)),
}, {
Ast.NumberExpression(1),
}),
Ast.ReturnStatement({
Ast.VariableExpression(self.scope, self.currentUpvalId),
})
}, scope));
end
end
================================================
FILE: src/prometheus/enums.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- enums.lua
--
-- This Script provides some enums used by the Obfuscator.
local Enums = {};
local chararray = require("prometheus.util").chararray;
Enums.LuaVersion = {
LuaU = "LuaU" ,
Lua51 = "Lua51",
}
Enums.Conventions = {
[Enums.LuaVersion.Lua51] = {
Keywords = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or",
"repeat", "return", "then", "true", "until", "while"
},
SymbolChars = chararray("+-*/%^#=~<>(){}[];:,."),
MaxSymbolLength = 3,
Symbols = {
"+", "-", "*", "/", "%", "^", "#",
"==", "~=", "<=", ">=", "<", ">", "=",
"(", ")", "{", "}", "[", "]",
";", ":", ",", ".", "..", "...",
},
IdentChars = chararray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"),
NumberChars = chararray("0123456789"),
HexNumberChars = chararray("0123456789abcdefABCDEF"),
BinaryNumberChars = {"0", "1"},
DecimalExponent = {"e", "E"},
HexadecimalNums = {"x", "X"},
BinaryNums = {"b", "B"},
DecimalSeperators = false,
EscapeSequences = {
["a"] = "\a";
["b"] = "\b";
["f"] = "\f";
["n"] = "\n";
["r"] = "\r";
["t"] = "\t";
["v"] = "\v";
["\\"] = "\\";
["\""] = "\"";
["\'"] = "\'";
},
NumericalEscapes = true,
EscapeZIgnoreNextWhitespace = true,
HexEscapes = true,
UnicodeEscapes = true,
},
[Enums.LuaVersion.LuaU] = {
Keywords = {
"and", "break", "do", "else", "elseif", "continue",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or",
"repeat", "return", "then", "true", "until", "while"
},
SymbolChars = chararray("+-*/%^#=~<>(){}[];:,."),
MaxSymbolLength = 3,
Symbols = {
"+", "-", "*", "/", "%", "^", "#",
"==", "~=", "<=", ">=", "<", ">", "=",
"+=", "-=", "/=", "%=", "^=", "..=", "*=",
"(", ")", "{", "}", "[", "]",
";", ":", ",", ".", "..", "...",
"::", "->", "?", "|", "&",
},
IdentChars = chararray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"),
NumberChars = chararray("0123456789"),
HexNumberChars = chararray("0123456789abcdefABCDEF"),
BinaryNumberChars = {"0", "1"},
DecimalExponent = {"e", "E"},
HexadecimalNums = {"x", "X"},
BinaryNums = {"b", "B"},
DecimalSeperators = {"_"},
EscapeSequences = {
["a"] = "\a";
["b"] = "\b";
["f"] = "\f";
["n"] = "\n";
["r"] = "\r";
["t"] = "\t";
["v"] = "\v";
["\\"] = "\\";
["\""] = "\"";
["\'"] = "\'";
},
NumericalEscapes = true,
EscapeZIgnoreNextWhitespace = true,
HexEscapes = true,
UnicodeEscapes = true,
},
}
return Enums;
================================================
FILE: src/prometheus/namegenerators/Il.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- namegenerators/il.lua
--
-- This Script provides a function for generation of weird names consisting of I, l and 1
local MIN_CHARACTERS = 5;
local MAX_INITIAL_CHARACTERS = 10;
local util = require("prometheus.util");
local chararray = util.chararray;
local offset = 0;
local VarDigits = chararray("Il1");
local VarStartDigits = chararray("Il");
local function generateName(id, _)
local name = ''
id = id + offset;
local d = id % #VarStartDigits
id = (id - d) / #VarStartDigits
name = name..VarStartDigits[d+1]
while id > 0 do
local e = id % #VarDigits
id = (id - e) / #VarDigits
name = name..VarDigits[e+1]
end
return name
end
local function prepare(_)
util.shuffle(VarDigits);
util.shuffle(VarStartDigits);
offset = math.random(3 ^ MIN_CHARACTERS, 3 ^ MAX_INITIAL_CHARACTERS);
end
return {
generateName = generateName,
prepare = prepare
};
================================================
FILE: src/prometheus/namegenerators/confuse.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- namegenerators/confuse.lua
--
-- This Script provides a function for generation of confusing variable names
local util = require("prometheus.util");
local varNames = {
"index",
"iterator",
"length",
"size",
"key",
"value",
"data",
"count",
"increment",
"include",
"string",
"number",
"type",
"void",
"int",
"float",
"bool",
"char",
"double",
"long",
"short",
"unsigned",
"signed",
"program",
"factory",
"Factory",
"new",
"delete",
"table",
"array",
"object",
"class",
"arr",
"obj",
"cls",
"dir",
"directory",
"isWindows",
"isLinux",
"game",
"roblox",
"gmod",
"gsub",
"gmatch",
"gfind",
"onload",
"load",
"loadstring",
"loadfile",
"dofile",
"require",
"parse",
"byte",
"code",
"bytecode",
"idx",
"const",
"loader",
"loaders",
"module",
"export",
"exports",
"import",
"imports",
"package",
"packages",
"_G",
"math",
"os",
"io",
"write",
"print",
"read",
"readline",
"readlines",
"close",
"flush",
"open",
"popen",
"tmpfile",
"tmpname",
"rename",
"remove",
"seek",
"setvbuf",
"lines",
"call",
"apply",
"raise",
"pcall",
"xpcall",
"coroutine",
"create",
"resume",
"status",
"wrap",
"yield",
"debug",
"traceback",
"getinfo",
"getlocal",
"setlocal",
"getupvalue",
"setupvalue",
"getuservalue",
"setuservalue",
"upvalueid",
"upvaluejoin",
"sethook",
"gethook",
"hookfunction",
"hooks",
"error",
"setmetatable",
"getmetatable",
"rand",
"randomseed",
"next",
"ipairs",
"hasnext",
"loadlib",
"searchpath",
"oldpath",
"newpath",
"path",
"rawequal",
"rawset",
"rawget",
"rawnew",
"rawlen",
"select",
"tonumber",
"tostring",
"assert",
"collectgarbage",
"a", "b", "c", "i", "j", "m",
}
local function generateName(id, _)
local name = {};
local d = id % #varNames
id = (id - d) / #varNames
table.insert(name, varNames[d + 1]);
while id > 0 do
local e = id % #varNames
id = (id - e) / #varNames
table.insert(name, varNames[e + 1]);
end
return table.concat(name, "_");
end
local function prepare(_)
util.shuffle(varNames);
end
return {
generateName = generateName,
prepare = prepare
};
================================================
FILE: src/prometheus/namegenerators/mangled.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- namegenerators/mangled.lua
--
-- This Script provides a function for generation of mangled names
local util = require("prometheus.util");
local chararray = util.chararray;
local VarDigits = chararray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
local VarStartDigits = chararray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
return function(id, _)
local name = ''
local d = id % #VarStartDigits
id = (id - d) / #VarStartDigits
name = name..VarStartDigits[d+1]
while id > 0 do
local e = id % #VarDigits
id = (id - e) / #VarDigits
name = name..VarDigits[e+1]
end
return name
end
================================================
FILE: src/prometheus/namegenerators/mangled_shuffled.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- namegenerators/mangled_shuffled.lua
--
-- This Script provides a function for generation of mangled names with shuffled character order
local util = require("prometheus.util");
local chararray = util.chararray;
local VarDigits = chararray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
local VarStartDigits = chararray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
local function generateName(id, _)
local name = ''
local d = id % #VarStartDigits
id = (id - d) / #VarStartDigits
name = name..VarStartDigits[d+1]
while id > 0 do
local e = id % #VarDigits
id = (id - e) / #VarDigits
name = name..VarDigits[e+1]
end
return name
end
local function prepare(_)
util.shuffle(VarDigits);
util.shuffle(VarStartDigits);
end
return {
generateName = generateName,
prepare = prepare
};
================================================
FILE: src/prometheus/namegenerators/number.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- namegenerators/number.lua
--
-- This Script provides a function for generation of simple up counting names but with hex numbers
local PREFIX = "_";
return function(id, _)
return PREFIX .. tostring(id);
end
================================================
FILE: src/prometheus/namegenerators.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- namegenerators.lua
--
-- This Script provides a collection of name generators for Prometheus.
return {
Mangled = require("prometheus.namegenerators.mangled");
MangledShuffled = require("prometheus.namegenerators.mangled_shuffled");
Il = require("prometheus.namegenerators.Il");
Number = require("prometheus.namegenerators.number");
Confuse = require("prometheus.namegenerators.confuse");
}
================================================
FILE: src/prometheus/parser.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- parser.lua
-- Overview:
-- This Script provides a class for parsing of lua code.
-- This Parser is Capable of parsing LuaU and Lua5.1
--
-- Note that when parsing LuaU "continue" is treated as a Keyword, so no variable may be named "continue" even though this would be valid in LuaU
--
-- Settings Object:
-- luaVersion : The LuaVersion of the Script - Currently Supported : Lua51 and LuaU
--
local Tokenizer = require("prometheus.tokenizer");
local Enums = require("prometheus.enums");
local util = require("prometheus.util");
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local logger = require("logger");
local AstKind = Ast.AstKind;
local LuaVersion = Enums.LuaVersion;
local lookupify = util.lookupify;
local TokenKind = Tokenizer.TokenKind;
local Parser = {};
local ASSIGNMENT_NO_WARN_LOOKUP = lookupify{
AstKind.NilExpression,
AstKind.FunctionCallExpression,
AstKind.PassSelfFunctionCallExpression,
AstKind.VarargExpression
};
local CALLABLE_PREFIX_EXPRESSION_LOOKUP = lookupify{
AstKind.VariableExpression,
AstKind.IndexExpression,
AstKind.FunctionCallExpression,
AstKind.PassSelfFunctionCallExpression
};
local function generateError(self, message)
local token;
if(self.index > self.length) then
token = self.tokens[self.length];
elseif(self.index < 1) then
return "Parsing Error at Position 0:0, " .. message;
else
token = self.tokens[self.index];
end
return "Parsing Error at Position " .. tostring(token.line) .. ":" .. tostring(token.linePos) .. ", " .. message;
end
local function generateWarning(token, message)
return "Warning at Position " .. tostring(token.line) .. ":" .. tostring(token.linePos) .. ", " .. message;
end
function Parser:new(settings)
local luaVersion = (settings and (settings.luaVersion or settings.LuaVersion)) or LuaVersion.LuaU;
local parser = {
luaVersion = luaVersion,
tokenizer = Tokenizer:new({
luaVersion = luaVersion
}),
tokens = {};
length = 0;
index = 0;
};
setmetatable(parser, self);
self.__index = self;
return parser;
end
-- Function to peek the n'th token
local function peek(self, n)
n = n or 0;
local i = self.index + n + 1;
if i > self.length then
return Tokenizer.EOF_TOKEN;
end
return self.tokens[i];
end
-- Function to get the next Token
local function get(self)
local i = self.index + 1;
if i > self.length then
error(generateError(self, "Unexpected end of Input"));
end
self.index = self.index + 1;
local tk = self.tokens[i];
return tk;
end
local function is(self, kind, sourceOrN, n)
local token = peek(self, n);
local source = nil;
if(type(sourceOrN) == "string") then
source = sourceOrN;
else
n = sourceOrN;
end
n = n or 0;
if(token.kind == kind) then
if(source == nil or token.source == source) then
return true;
end
end
return false;
end
local function consume(self, kind, source)
if(is(self, kind, source)) then
self.index = self.index + 1;
return true;
end
return false;
end
local function expect(self, kind, source)
if(is(self, kind, source, 0)) then
return get(self);
end
local token = peek(self);
if self.disableLog then error() end
if(source) then
logger:error(generateError(self, string.format("unexpected token <%s> \"%s\", expected <%s> \"%s\"", token.kind, token.source, kind, source)));
else
logger:error(generateError(self, string.format("unexpected token <%s> \"%s\", expected <%s>", token.kind, token.source, kind)));
end
end
-- Parse the given code to an Abstract Syntax Tree
function Parser:parse(code)
self.tokenizer:append(code);
self.tokens = self.tokenizer:scanAll();
self.length = #self.tokens;
-- Create Global Variable Scope
local globalScope = Scope:newGlobal();
local ast = Ast.TopNode(self:block(globalScope, false), globalScope);
-- File Must be Over when Top Node is Fully Parsed
expect(self, TokenKind.Eof);
logger:debug("Cleaning up Parser for next Use ...")
-- Clean Up
self.tokenizer:reset();
self.tokens = {};
self.index = 0;
self.length = 0;
logger:debug("Cleanup Done")
return ast;
end
-- Parse a Code Block
function Parser:block(parentScope, currentLoop, scope)
scope = scope or Scope:new(parentScope);
local statements = {};
repeat
local statement, isTerminatingStatement = self:statement(scope, currentLoop);
table.insert(statements, statement);
until isTerminatingStatement or not statement
-- Consume Eventual Semicolon after terminating return, break or continue
consume(self, TokenKind.Symbol, ";");
return Ast.Block(statements, scope);
end
function Parser:statement(scope, currentLoop)
-- Skip all semicolons before next real statement
-- NOP statements are therefore ignored
while(consume(self, TokenKind.Symbol, ";")) do
end
-- Break Statement - only valid inside of Loops
if(consume(self, TokenKind.Keyword, "break")) then
if(not currentLoop) then
if self.disableLog then error() end;
logger:error(generateError(self, "the break Statement is only valid inside of loops"));
end
-- Return true as Second value because break must be the last Statement in a block
return Ast.BreakStatement(currentLoop, scope), true;
end
-- Continue Statement - only valid inside of Loops - only valid in LuaU
if(self.luaVersion == LuaVersion.LuaU and consume(self, TokenKind.Keyword, "continue")) then
if(not currentLoop) then
if self.disableLog then error() end;
logger:error(generateError(self, "the continue Statement is only valid inside of loops"));
end
-- Return true as Second value because continue must be the last Statement in a block
return Ast.ContinueStatement(currentLoop, scope), true;
end
-- do ... end Statement
if(consume(self, TokenKind.Keyword, "do")) then
local body = self:block(scope, currentLoop);
expect(self, TokenKind.Keyword, "end");
return Ast.DoStatement(body);
end
-- While Statement
if(consume(self, TokenKind.Keyword, "while")) then
local condition = self:expression(scope);
expect(self, TokenKind.Keyword, "do");
local stat = Ast.WhileStatement(nil, condition, scope);
stat.body = self:block(scope, stat);
expect(self, TokenKind.Keyword, "end");
return stat;
end
-- Repeat Statement
if(consume(self, TokenKind.Keyword, "repeat")) then
local repeatScope = Scope:new(scope);
local stat = Ast.RepeatStatement(nil, nil, scope);
stat.body = self:block(nil, stat, repeatScope);
expect(self, TokenKind.Keyword, "until");
stat.condition = self:expression(repeatScope);
return stat;
end
-- Return Statement
if(consume(self, TokenKind.Keyword, "return")) then
local args = {};
if(not is(self, TokenKind.Keyword, "end") and not is(self, TokenKind.Keyword, "elseif") and not is(self, TokenKind.Keyword, "else") and not is(self, TokenKind.Symbol, ";") and not is(self, TokenKind.Eof)) then
args = self:exprList(scope);
end
-- Return true as Second value because return must be the last Statement in a block
return Ast.ReturnStatement(args), true;
end
-- If Statement
if(consume(self, TokenKind.Keyword, "if")) then
local condition = self:expression(scope);
expect(self, TokenKind.Keyword, "then");
local body = self:block(scope, currentLoop);
local elseifs = {};
-- Elseifs
while(consume(self, TokenKind.Keyword, "elseif")) do
local condition = self:expression(scope);
expect(self, TokenKind.Keyword, "then");
local body = self:block(scope, currentLoop);
table.insert(elseifs, {
condition = condition,
body = body,
});
end
local elsebody = nil;
-- Else
if(consume(self, TokenKind.Keyword, "else")) then
elsebody = self:block(scope, currentLoop);
end
expect(self, TokenKind.Keyword, "end");
return Ast.IfStatement(condition, body, elseifs, elsebody);
end
-- Function Declaration
if(consume(self, TokenKind.Keyword, "function")) then
-- TODO: Parse Function Declaration Name
local obj = self:funcName(scope);
local baseScope = obj.scope;
local baseId = obj.id;
local indices = obj.indices;
local funcScope = Scope:new(scope);
expect(self, TokenKind.Symbol, "(");
local args = self:functionArgList(funcScope);
expect(self, TokenKind.Symbol, ")");
if(obj.passSelf) then
local id = funcScope:addVariable("self", obj.token);
table.insert(args, 1, Ast.VariableExpression(funcScope, id));
end
local body = self:block(nil, false, funcScope);
expect(self, TokenKind.Keyword, "end");
return Ast.FunctionDeclaration(baseScope, baseId, indices, args, body);
end
-- Local Function or Variable Declaration
if(consume(self, TokenKind.Keyword, "local")) then
-- Local Function Declaration
if(consume(self, TokenKind.Keyword, "function")) then
local ident = expect(self, TokenKind.Ident);
local name = ident.value;
local id = scope:addVariable(name, ident);
local funcScope = Scope:new(scope);
expect(self, TokenKind.Symbol, "(");
local args = self:functionArgList(funcScope);
expect(self, TokenKind.Symbol, ")");
local body = self:block(nil, false, funcScope);
expect(self, TokenKind.Keyword, "end");
return Ast.LocalFunctionDeclaration(scope, id, args, body);
end
-- Local Variable Declaration
local ids = self:nameList(scope);
local expressions = {};
if(consume(self, TokenKind.Symbol, "=")) then
expressions = self:exprList(scope);
end
-- Variables can only be reffered to in the next statement, so the id's are enabled after the expressions have been parsed
self:enableNameList(scope, ids);
if(#expressions > #ids) then
logger:warn(generateWarning(peek(self, -1), string.format("assigning %d values to %d variable" .. ((#ids > 1 and "s") or ""), #expressions, #ids)));
elseif(#ids > #expressions and #expressions > 0 and not ASSIGNMENT_NO_WARN_LOOKUP[expressions[#expressions].kind]) then
logger:warn(generateWarning(peek(self, -1), string.format("assigning %d value" .. ((#expressions > 1 and "s") or "") ..
" to %d variables initializes extra variables with nil, add a nil value to silence", #expressions, #ids)));
end
return Ast.LocalVariableDeclaration(scope, ids, expressions);
end
-- For Statement
if(consume(self, TokenKind.Keyword, "for")) then
-- Normal for Statement
if(is(self, TokenKind.Symbol, "=", 1)) then
local forScope = Scope:new(scope);
local ident = expect(self, TokenKind.Ident);
local varId = forScope:addDisabledVariable(ident.value, ident);
expect(self, TokenKind.Symbol, "=");
local initialValue = self:expression(scope);
expect(self, TokenKind.Symbol, ",");
local finalValue = self:expression(scope);
local incrementBy = Ast.NumberExpression(1);
if(consume(self, TokenKind.Symbol, ",")) then
incrementBy = self:expression(scope);
end
local stat = Ast.ForStatement(forScope, varId, initialValue, finalValue, incrementBy, nil, scope);
forScope:enableVariable(varId);
expect(self, TokenKind.Keyword, "do");
stat.body = self:block(nil, stat, forScope);
expect(self, TokenKind.Keyword, "end");
return stat;
end
-- For ... in ... statement
local forScope = Scope:new(scope);
local ids = self:nameList(forScope);
expect(self, TokenKind.Keyword, "in");
local expressions = self:exprList(scope);
-- Enable Ids after Expression Parsing so that code like this works:
-- local z = {10,20}
-- for y,z in ipairs(z) do
-- print(y, z);
-- end
self:enableNameList(forScope, ids);
expect(self, TokenKind.Keyword, "do");
local stat = Ast.ForInStatement(forScope, ids, expressions, nil, scope);
stat.body = self:block(nil, stat, forScope);
expect(self, TokenKind.Keyword, "end");
return stat;
end
local expr = self:primaryExpression(scope);
-- Variable Assignment or Function Call
if expr then
-- Function Call Statement
if(expr.kind == AstKind.FunctionCallExpression) then
return Ast.FunctionCallStatement(expr.base, expr.args);
end
-- Function Call Statement passing self
if(expr.kind == AstKind.PassSelfFunctionCallExpression) then
return Ast.PassSelfFunctionCallStatement(expr.base, expr.passSelfFunctionName, expr.args);
end
-- Variable Assignment
if(expr.kind == AstKind.IndexExpression or expr.kind == AstKind.VariableExpression) then
if(expr.kind == AstKind.IndexExpression) then
expr.kind = AstKind.AssignmentIndexing
end
if(expr.kind == AstKind.VariableExpression) then
expr.kind = AstKind.AssignmentVariable
end
if(self.luaVersion == LuaVersion.LuaU) then
-- LuaU Compound Assignment
if(consume(self, TokenKind.Symbol, "+=")) then
local rhs = self:expression(scope);
return Ast.CompoundAddStatement(expr, rhs);
end
if(consume(self, TokenKind.Symbol, "-=")) then
local rhs = self:expression(scope);
return Ast.CompoundSubStatement(expr, rhs);
end
if(consume(self, TokenKind.Symbol, "*=")) then
local rhs = self:expression(scope);
return Ast.CompoundMulStatement(expr, rhs);
end
if(consume(self, TokenKind.Symbol, "/=")) then
local rhs = self:expression(scope);
return Ast.CompoundDivStatement(expr, rhs);
end
if(consume(self, TokenKind.Symbol, "%=")) then
local rhs = self:expression(scope);
return Ast.CompoundModStatement(expr, rhs);
end
if(consume(self, TokenKind.Symbol, "^=")) then
local rhs = self:expression(scope);
return Ast.CompoundPowStatement(expr, rhs);
end
if(consume(self, TokenKind.Symbol, "..=")) then
local rhs = self:expression(scope);
return Ast.CompoundConcatStatement(expr, rhs);
end
end
local lhs = {
expr
}
while consume(self, TokenKind.Symbol, ",") do
expr = self:primaryExpression(scope);
if(not expr) then
if self.disableLog then error() end;
logger:error(generateError(self, string.format("expected a valid assignment statement lhs part but got nil")));
end
if(expr.kind == AstKind.IndexExpression or expr.kind == AstKind.VariableExpression) then
if(expr.kind == AstKind.IndexExpression) then
expr.kind = AstKind.AssignmentIndexing
end
if(expr.kind == AstKind.VariableExpression) then
expr.kind = AstKind.AssignmentVariable
end
table.insert(lhs, expr);
else
if self.disableLog then error() end;
logger:error(generateError(self, string.format("expected a valid assignment statement lhs part but got <%s>", expr.kind)));
end
end
expect(self, TokenKind.Symbol, "=");
local rhs = self:exprList(scope);
return Ast.AssignmentStatement(lhs, rhs);
end
if self.disableLog then error() end;
logger:error(generateError(self, "expressions are not valid statements!"));
end
return nil;
end
function Parser:primaryExpression(scope)
local i = self.index;
local s = self;
self.disableLog = true;
local status, val = pcall(self.expressionFunctionCall, self, scope);
self.disableLog = false;
if(status) then
return val;
else
self.index = i;
return nil;
end
end
-- List of expressions Seperated by a comma
function Parser:exprList(scope)
local expressions = {
self:expression(scope)
};
while(consume(self, TokenKind.Symbol, ",")) do
table.insert(expressions, self:expression(scope));
end
return expressions;
end
-- list of local variable names
function Parser:nameList(scope)
local ids = {};
local ident = expect(self, TokenKind.Ident);
local id = scope:addDisabledVariable(ident.value, ident);
table.insert(ids, id);
while(consume(self, TokenKind.Symbol, ",")) do
ident = expect(self, TokenKind.Ident);
id = scope:addDisabledVariable(ident.value, ident);
table.insert(ids, id);
end
return ids;
end
function Parser:enableNameList(scope, list)
for i, id in ipairs(list) do
scope:enableVariable(id);
end
end
-- function name
function Parser:funcName(scope)
local ident = expect(self, TokenKind.Ident);
local baseName = ident.value;
local baseScope, baseId = scope:resolve(baseName);
local indices = {};
local passSelf = false;
while(consume(self, TokenKind.Symbol, ".")) do
table.insert(indices, expect(self, TokenKind.Ident).value);
end
if(consume(self, TokenKind.Symbol, ":")) then
table.insert(indices, expect(self, TokenKind.Ident).value);
passSelf = true;
end
return {
scope = baseScope,
id = baseId,
indices = indices,
passSelf = passSelf,
token = ident,
};
end
-- Expression
function Parser:expression(scope)
return self:expressionOr(scope);
end
function Parser:expressionOr(scope)
local lhs = self:expressionAnd(scope);
if(consume(self, TokenKind.Keyword, "or")) then
local rhs = self:expressionOr(scope);
return Ast.OrExpression(lhs, rhs, true);
end
return lhs;
end
function Parser:expressionAnd(scope)
local lhs = self:expressionComparision(scope);
if(consume(self, TokenKind.Keyword, "and")) then
local rhs = self:expressionAnd(scope);
return Ast.AndExpression(lhs, rhs, true);
end
return lhs;
end
function Parser:expressionComparision(scope)
local curr = self:expressionStrCat(scope);
repeat
local found = false;
if(consume(self, TokenKind.Symbol, "<")) then
local rhs = self:expressionStrCat(scope);
curr = Ast.LessThanExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, ">")) then
local rhs = self:expressionStrCat(scope);
curr = Ast.GreaterThanExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, "<=")) then
local rhs = self:expressionStrCat(scope);
curr = Ast.LessThanOrEqualsExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, ">=")) then
local rhs = self:expressionStrCat(scope);
curr = Ast.GreaterThanOrEqualsExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, "~=")) then
local rhs = self:expressionStrCat(scope);
curr = Ast.NotEqualsExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, "==")) then
local rhs = self:expressionStrCat(scope);
curr = Ast.EqualsExpression(curr, rhs, true);
found = true;
end
until not found;
return curr;
end
function Parser:expressionStrCat(scope)
local lhs = self:expressionAddSub(scope);
if(consume(self, TokenKind.Symbol, "..")) then
local rhs = self:expressionStrCat(scope);
return Ast.StrCatExpression(lhs, rhs, true);
end
return lhs;
end
function Parser:expressionAddSub(scope)
local curr = self:expressionMulDivMod(scope);
repeat
local found = false;
if(consume(self, TokenKind.Symbol, "+")) then
local rhs = self:expressionMulDivMod(scope);
curr = Ast.AddExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, "-")) then
local rhs = self:expressionMulDivMod(scope);
curr = Ast.SubExpression(curr, rhs, true);
found = true;
end
until not found;
return curr;
end
function Parser:expressionMulDivMod(scope)
local curr = self:expressionUnary(scope);
repeat
local found = false;
if(consume(self, TokenKind.Symbol, "*")) then
local rhs = self:expressionUnary(scope);
curr = Ast.MulExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, "/")) then
local rhs = self:expressionUnary(scope);
curr = Ast.DivExpression(curr, rhs, true);
found = true;
end
if(consume(self, TokenKind.Symbol, "%")) then
local rhs = self:expressionUnary(scope);
curr = Ast.ModExpression(curr, rhs, true);
found = true;
end
until not found;
return curr;
end
function Parser:expressionUnary(scope)
if(consume(self, TokenKind.Keyword, "not")) then
local rhs = self:expressionUnary(scope);
return Ast.NotExpression(rhs, true);
end
if(consume(self, TokenKind.Symbol, "#")) then
local rhs = self:expressionUnary(scope);
return Ast.LenExpression(rhs, true);
end
if(consume(self, TokenKind.Symbol, "-")) then
local rhs = self:expressionUnary(scope);
return Ast.NegateExpression(rhs, true);
end
return self:expressionPow(scope);
end
function Parser:expressionPow(scope)
local lhs = self:tableOrFunctionLiteral(scope);
if(consume(self, TokenKind.Symbol, "^")) then
-- Allow unary operators on the rhs (e.g. 2 ^ #x, 2 ^ -x) while preserving right-associativity. ~ SpinnySpiwal
local rhs = self:expressionUnary(scope);
return Ast.PowExpression(lhs, rhs, true);
end
return lhs;
end
-- Table Literals and Function Literals cannot directly be called or indexed
function Parser:tableOrFunctionLiteral(scope)
if(is(self, TokenKind.Symbol, "{")) then
return self:tableConstructor(scope);
end
if(is(self, TokenKind.Keyword, "function")) then
return self:expressionFunctionLiteral(scope);
end
return self:expressionFunctionCall(scope);
end
function Parser:expressionFunctionLiteral(parentScope)
local scope = Scope:new(parentScope);
expect(self, TokenKind.Keyword, "function");
expect(self, TokenKind.Symbol, "(");
local args = self:functionArgList(scope);
expect(self, TokenKind.Symbol, ")");
local body = self:block(nil, false, scope);
expect(self, TokenKind.Keyword, "end");
return Ast.FunctionLiteralExpression(args, body);
end
function Parser:functionArgList(scope)
local args = {};
if(consume(self, TokenKind.Symbol, "...")) then
table.insert(args, Ast.VarargExpression());
return args;
end
if(is(self, TokenKind.Ident)) then
local ident = get(self);
local name = ident.value;
local id = scope:addVariable(name, ident);
table.insert(args, Ast.VariableExpression(scope, id));
while(consume(self, TokenKind.Symbol, ",")) do
if(consume(self, TokenKind.Symbol, "...")) then
table.insert(args, Ast.VarargExpression());
return args;
end
ident = get(self);
name = ident.value;
id = scope:addVariable(name, ident);
table.insert(args, Ast.VariableExpression(scope, id));
end
end
return args;
end
function Parser:expressionFunctionCall(scope, base)
base = base or self:expressionIndex(scope);
if(not (base and (CALLABLE_PREFIX_EXPRESSION_LOOKUP[base.kind] or base.isParenthesizedExpression))) then
return base;
end
-- Normal Function Call
local args = {};
if(is(self, TokenKind.String)) then
args = {
Ast.StringExpression(get(self).value),
};
elseif(is(self, TokenKind.Symbol, "{")) then
args = {
self:tableConstructor(scope),
};
elseif(consume(self, TokenKind.Symbol, "(")) then
if(not is(self, TokenKind.Symbol, ")")) then
args = self:exprList(scope);
end
expect(self, TokenKind.Symbol, ")");
else
return base;
end
local node = Ast.FunctionCallExpression(base, args);
-- the result of a function call can be indexed
if(is(self, TokenKind.Symbol, ".") or is(self, TokenKind.Symbol, "[") or is(self, TokenKind.Symbol, ":")) then
return self:expressionIndex(scope, node);
end
-- The result of a function call can be a function that is again called
if(is(self, TokenKind.Symbol, "(") or is(self, TokenKind.Symbol, "{") or is(self, TokenKind.String)) then
return self:expressionFunctionCall(scope, node);
end
return node;
end
function Parser:expressionIndex(scope, base)
base = base or self:expressionLiteral(scope);
-- Parse Indexing Expressions
while(consume(self, TokenKind.Symbol, "[")) do
local expr = self:expression(scope);
expect(self, TokenKind.Symbol, "]");
base = Ast.IndexExpression(base, expr);
end
-- Parse Indexing Expressions
while consume(self, TokenKind.Symbol, ".") do
local ident = expect(self, TokenKind.Ident);
base = Ast.IndexExpression(base, Ast.StringExpression(ident.value));
while(consume(self, TokenKind.Symbol, "[")) do
local expr = self:expression(scope);
expect(self, TokenKind.Symbol, "]");
base = Ast.IndexExpression(base, expr);
end
end
-- Function Passing self
if(consume(self, TokenKind.Symbol, ":")) then
local passSelfFunctionName = expect(self, TokenKind.Ident).value;
local args = {};
if(is(self, TokenKind.String)) then
args = {
Ast.StringExpression(get(self).value),
};
elseif(is(self, TokenKind.Symbol, "{")) then
args = {
self:tableConstructor(scope),
};
else
expect(self, TokenKind.Symbol, "(");
if(not is(self, TokenKind.Symbol, ")")) then
args = self:exprList(scope);
end
expect(self, TokenKind.Symbol, ")");
end
local node = Ast.PassSelfFunctionCallExpression(base, passSelfFunctionName, args);
-- the result of a function call can be indexed
if(is(self, TokenKind.Symbol, ".") or is(self, TokenKind.Symbol, "[") or is(self, TokenKind.Symbol, ":")) then
return self:expressionIndex(scope, node);
end
-- The result of a function call can be a function that is again called
if(is(self, TokenKind.Symbol, "(") or is(self, TokenKind.Symbol, "{") or is(self, TokenKind.String)) then
return self:expressionFunctionCall(scope, node);
end
return node
end
-- The result of a function call can be a function that is again called
if(is(self, TokenKind.Symbol, "(") or is(self, TokenKind.Symbol, "{") or is(self, TokenKind.String)) then
return self:expressionFunctionCall(scope, base);
end
return base;
end
function Parser:expressionLiteral(scope)
-- () expression
if(consume(self, TokenKind.Symbol, "(")) then
local expr = self:expression(scope);
expect(self, TokenKind.Symbol, ")");
if expr then
expr.isParenthesizedExpression = true;
end
return expr;
end
-- String Literal
if(is(self, TokenKind.String)) then
return Ast.StringExpression(get(self).value);
end
-- Number Literal
if(is(self, TokenKind.Number)) then
return Ast.NumberExpression(get(self).value);
end
-- True Literal
if(consume(self, TokenKind.Keyword, "true")) then
return Ast.BooleanExpression(true);
end
-- False Literal
if(consume(self, TokenKind.Keyword, "false")) then
return Ast.BooleanExpression(false);
end
-- Nil Literal
if(consume(self, TokenKind.Keyword, "nil")) then
return Ast.NilExpression();
end
-- Vararg Literal
if(consume(self, TokenKind.Symbol, "...")) then
return Ast.VarargExpression();
end
-- Variable
if(is(self, TokenKind.Ident)) then
local ident = get(self);
local name = ident.value;
local scope, id = scope:resolve(name);
return Ast.VariableExpression(scope, id);
end
-- IfElse
if(LuaVersion.LuaU) then
if(consume(self, TokenKind.Keyword, "if")) then
local condition = self:expression(scope);
expect(self, TokenKind.Keyword, "then");
local true_value = self:expression(scope);
expect(self, TokenKind.Keyword, "else");
local false_value = self:expression(scope);
return Ast.IfElseExpression(condition, true_value, false_value);
end
end
if(self.disableLog) then error() end
logger:error(generateError(self, "Unexpected Token \"" .. peek(self).source .. "\". Expected a Expression!"))
end
function Parser:tableConstructor(scope)
-- TODO: Parse Table Literals
local entries = {};
expect(self, TokenKind.Symbol, "{");
while (not consume(self, TokenKind.Symbol, "}")) do
if(consume(self, TokenKind.Symbol, "[")) then
local key = self:expression(scope);
expect(self, TokenKind.Symbol, "]");
expect(self, TokenKind.Symbol, "=");
local value = self:expression(scope);
table.insert(entries, Ast.KeyedTableEntry(key, value));
elseif(is(self, TokenKind.Ident, 0) and is(self, TokenKind.Symbol, "=", 1)) then
local key = Ast.StringExpression(get(self).value);
expect(self, TokenKind.Symbol, "=");
local value = self:expression(scope);
table.insert(entries, Ast.KeyedTableEntry(key, value));
else
local value = self:expression(scope);
table.insert(entries, Ast.TableEntry(value));
end
if (not consume(self, TokenKind.Symbol, ";") and not consume(self, TokenKind.Symbol, ",") and not is(self, TokenKind.Symbol, "}")) then
if self.disableLog then error() end
logger:error(generateError(self, "expected a \";\" or a \",\""));
end
end
return Ast.TableConstructorExpression(entries);
end
return Parser
================================================
FILE: src/prometheus/pipeline.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- pipeline.lua
--
-- This Script provides a configurable obfuscation pipeline that can obfuscate code using different modules
-- These modules can simply be added to the pipeline.
local Enums = require("prometheus.enums");
local util = require("prometheus.util");
local Parser = require("prometheus.parser");
local Unparser = require("prometheus.unparser");
local logger = require("logger");
local NameGenerators = require("prometheus.namegenerators");
local Steps = require("prometheus.steps");
local LuaVersion = Enums.LuaVersion;
-- On Windows, os.clock can be used. On other systems, os.time must be used for benchmarking.
local isWindows = package and package.config and type(package.config) == "string" and package.config:sub(1,1) == "\\";
local function gettime()
if isWindows then
return os.clock();
else
return os.time();
end
end
local Pipeline = {
NameGenerators = NameGenerators;
Steps = Steps;
DefaultSettings = {
LuaVersion = LuaVersion.LuaU; -- The Lua Version to use for the Tokenizer, Parser and Unparser
PrettyPrint = false; -- Note that Pretty Print is currently not producing Pretty results
Seed = 0; -- The Seed. 0 or below uses the current time as a seed
VarNamePrefix = ""; -- The Prefix that every variable will start with
}
}
function Pipeline:new(settings)
local luaVersion = settings.luaVersion or settings.LuaVersion or Pipeline.DefaultSettings.LuaVersion;
local conventions = Enums.Conventions[luaVersion];
if(not conventions) then
logger:error("The Lua Version \"" .. luaVersion
.. "\" is not recognized by the Tokenizer! Please use one of the following: \"" .. table.concat(util.keys(Enums.Conventions), "\",\"") .. "\"");
end
local prettyPrint = settings.PrettyPrint or Pipeline.DefaultSettings.PrettyPrint;
local prefix = settings.VarNamePrefix or Pipeline.DefaultSettings.VarNamePrefix;
local seed = settings.Seed or 0;
local pipeline = {
LuaVersion = luaVersion;
PrettyPrint = prettyPrint;
VarNamePrefix = prefix;
Seed = seed;
parser = Parser:new({
LuaVersion = luaVersion;
});
unparser = Unparser:new({
LuaVersion = luaVersion;
PrettyPrint = prettyPrint;
Highlight = settings.Highlight;
});
namegenerator = Pipeline.NameGenerators.MangledShuffled;
conventions = conventions;
steps = {};
}
setmetatable(pipeline, self);
self.__index = self;
return pipeline;
end
function Pipeline:fromConfig(config)
config = config or {};
local pipeline = Pipeline:new({
LuaVersion = config.LuaVersion or LuaVersion.Lua51;
PrettyPrint = config.PrettyPrint or false;
VarNamePrefix = config.VarNamePrefix or "";
Seed = config.Seed or 0;
});
pipeline:setNameGenerator(config.NameGenerator or "MangledShuffled")
-- Add all Steps defined in Config
local steps = config.Steps or {};
for i, step in ipairs(steps) do
if type(step.Name) ~= "string" then
logger:error("Step.Name must be a String");
end
local constructor = pipeline.Steps[step.Name];
if not constructor then
logger:error(string.format("The Step \"%s\" was not found!", step.Name));
end
pipeline:addStep(constructor:new(step.Settings or {}));
end
return pipeline;
end
function Pipeline:addStep(step)
table.insert(self.steps, step);
end
function Pipeline:resetSteps(_)
self.steps = {};
end
function Pipeline:getSteps()
return self.steps;
end
function Pipeline:setOption(name, _)
assert(false, "TODO");
if(Pipeline.DefaultSettings[name] ~= nil) then
else
logger:error(string.format("\"%s\" is not a valid setting"));
end
end
function Pipeline:setLuaVersion(luaVersion)
local conventions = Enums.Conventions[luaVersion];
if(not conventions) then
logger:error("The Lua Version \"" .. luaVersion
.. "\" is not recognized by the Tokenizer! Please use one of the following: \"" .. table.concat(util.keys(Enums.Conventions), "\",\"") .. "\"");
end
self.parser = Parser:new({
luaVersion = luaVersion;
});
self.unparser = Unparser:new({
luaVersion = luaVersion;
});
self.conventions = conventions;
end
function Pipeline:getLuaVersion()
return self.luaVersion;
end
function Pipeline:setNameGenerator(nameGenerator)
if(type(nameGenerator) == "string") then
nameGenerator = Pipeline.NameGenerators[nameGenerator];
end
if(type(nameGenerator) == "function" or type(nameGenerator) == "table") then
self.namegenerator = nameGenerator;
return;
else
logger:error("The Argument to Pipeline:setNameGenerator must be a valid NameGenerator function or function name e.g: \"mangled\"")
end
end
function Pipeline:apply(code, filename)
local startTime = gettime();
filename = filename or "Anonymous Script";
logger:info(string.format("Applying Obfuscation Pipeline to %s ...", filename));
-- Seed the Random Generator
if(self.Seed > 0) then
math.randomseed(self.Seed);
else
--> use secure random number generator
local success, seed = pcall(function()
local seedStr = io.popen("openssl rand -hex 12"):read("*a"):gsub("\n", "")..""
local seedNum = 0;
--> NOTE: tonumber caps at 1.844674407371e+19. So we use this instead.
for i = 1, #seedStr do
local char = seedStr:sub(i, i):lower()
local digit = char:match("%d") and (char:byte() - 48) or (char:byte() - 87)
seedNum = seedNum * 16 + digit
end
--> Random Number Generator in Lua 5.1 is limited to 9.007199254741e+15.
if _VERSION == "Lua 5.1" and not jit then
seedNum = seedNum % 9.007199254741e+15
end
return seedNum
end)
if success then
math.randomseed(seed)
else
logger:warn("OpenSSL is unavailable. Falling back to unix time.");
math.randomseed(os.time())
end
end
logger:info("Parsing ...");
local parserStartTime = gettime();
local sourceLen = string.len(code);
local ast = self.parser:parse(code);
local parserTimeDiff = gettime() - parserStartTime;
logger:info(string.format("Parsing Done in %.2f seconds", parserTimeDiff));
-- User Defined Steps
for i, step in ipairs(self.steps) do
local stepStartTime = gettime();
logger:info(string.format("Applying Step \"%s\" ...", step.Name or "Unnamed"));
local newAst = step:apply(ast, self);
if type(newAst) == "table" then
ast = newAst;
end
logger:info(string.format("Step \"%s\" Done in %.2f seconds", step.Name or "Unnamed", gettime() - stepStartTime));
end
-- Rename Variables Step
self:renameVariables(ast);
code = self:unparse(ast);
local timeDiff = gettime() - startTime;
logger:info(string.format("Obfuscation Done in %.2f seconds", timeDiff));
logger:info(string.format("Generated Code size is %.2f%% of the Source Code size", (string.len(code) / sourceLen)*100))
return code;
end
function Pipeline:unparse(ast)
local startTime = gettime();
logger:info("Generating Code ...");
local unparsed = self.unparser:unparse(ast);
local timeDiff = gettime() - startTime;
logger:info(string.format("Code Generation Done in %.2f seconds", timeDiff));
return unparsed;
end
function Pipeline:renameVariables(ast)
local startTime = gettime();
logger:info("Renaming Variables ...");
local generatorFunction = self.namegenerator or Pipeline.NameGenerators.mangled;
if(type(generatorFunction) == "table") then
if (type(generatorFunction.prepare) == "function") then
generatorFunction.prepare(ast);
end
generatorFunction = generatorFunction.generateName;
end
if not self.unparser:isValidIdentifier(self.VarNamePrefix) and #self.VarNamePrefix ~= 0 then
logger:error(string.format("The Prefix \"%s\" is not a valid Identifier in %s", self.VarNamePrefix, self.LuaVersion));
end
local globalScope = ast.globalScope;
globalScope:renameVariables({
Keywords = self.conventions.Keywords;
generateName = generatorFunction;
prefix = self.VarNamePrefix;
});
local timeDiff = gettime() - startTime;
logger:info(string.format("Renaming Done in %.2f seconds", timeDiff));
end
return Pipeline;
================================================
FILE: src/prometheus/randomLiterals.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- randomLiterals.lua
--
-- This Script provides a library for creating random literals
local Ast = require("prometheus.ast");
local RandomStrings = require("prometheus.randomStrings");
local RandomLiterals = {};
local function callNameGenerator(generatorFunction, ...)
if(type(generatorFunction) == "table") then
generatorFunction = generatorFunction.generateName;
end
return generatorFunction(...);
end
function RandomLiterals.String(pipeline)
return Ast.StringExpression(callNameGenerator(pipeline.namegenerator, math.random(1, 4096)));
end
function RandomLiterals.Dictionary()
return RandomStrings.randomStringNode(true);
end
function RandomLiterals.Number()
return Ast.NumberExpression(math.random(-8388608, 8388607));
end
function RandomLiterals.Any(pipeline)
local type = math.random(1, 3);
if type == 1 then
return RandomLiterals.String(pipeline);
elseif type == 2 then
return RandomLiterals.Number();
elseif type == 3 then
return RandomLiterals.Dictionary();
end
end
return RandomLiterals;
================================================
FILE: src/prometheus/randomStrings.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- randomStrings.lua
--
-- This Script provides a library for generating random strings
local Ast = require("prometheus.ast")
local utils = require("prometheus.util")
local charset = utils.chararray("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890")
local function randomString(wordsOrLen)
if type(wordsOrLen) == "table" then
return wordsOrLen[math.random(1, #wordsOrLen)];
end
wordsOrLen = wordsOrLen or math.random(2, 15);
if wordsOrLen > 0 then
return randomString(wordsOrLen - 1) .. charset[math.random(1, #charset)]
else
return ""
end
end
local function randomStringNode(wordsOrLen)
return Ast.StringExpression(randomString(wordsOrLen))
end
return {
randomString = randomString,
randomStringNode = randomStringNode,
}
================================================
FILE: src/prometheus/scope.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- scope.lua
--
-- This Script provides a class for the Scope of a Lua Script
local logger = require("logger");
local config = require("config");
local Scope = {};
local scopeI = 0;
local function nextName()
scopeI = scopeI + 1;
return "local_scope_" .. tostring(scopeI);
end
local function generateWarning(token, message)
return "Warning at Position " .. tostring(token.line) .. ":" .. tostring(token.linePos) .. ", " .. message;
end
-- Create a new Local Scope
function Scope:new(parentScope, name)
local scope = {
isGlobal = false,
parentScope = parentScope,
variables = {},
referenceCounts = {};
variablesLookup = {},
variablesFromHigherScopes = {},
skipIdLookup = {};
name = name or nextName(),
children = {},
level = parentScope.level and (parentScope.level + 1) or 1;
}
setmetatable(scope, self);
self.__index = self;
parentScope:addChild(scope);
return scope;
end
-- Create a new Global Scope
function Scope:newGlobal()
local scope = {
isGlobal = true,
parentScope = nil,
variables = {},
variablesLookup = {};
referenceCounts = {};
skipIdLookup = {};
name = "global_scope",
children = {},
level = 0,
};
setmetatable(scope, self);
self.__index = self;
return scope;
end
-- Returns the Parent Scope
function Scope:getParent(parentScope)
return self.parentScope;
end
function Scope:setParent(parentScope)
self.parentScope:removeChild(self);
parentScope:addChild(self);
self.parentScope = parentScope;
self.level = parentScope.level + 1;
end
local next_name_i = 1;
-- Adds a Variable to the scope and returns the variable id, if no name is passed then a name is generated
function Scope:addVariable(name, token)
if (not name) then
name = string.format("%s%i", config.IdentPrefix, next_name_i);
next_name_i = next_name_i + 1;
end
if self.variablesLookup[name] ~= nil then
if(token) then
logger:warn(generateWarning(token, "the variable \"" .. name .. "\" is already defined in that scope"));
else
logger:error(string.format("A variable with the name \"%s\" was already defined, you should have no variables starting with \"%s\"", name, config.IdentPrefix));
end
--return self.variablesLookup[name];
end
table.insert(self.variables, name);
local id = #self.variables;
self.variablesLookup[name] = id;
return id;
end
function Scope:enableVariable(id)
local name = self.variables[id];
self.variablesLookup[name] = id;
end
function Scope:addDisabledVariable(name, token)
if (not name) then
name = string.format("%s%i", config.IdentPrefix, next_name_i);
next_name_i = next_name_i + 1;
end
if self.variablesLookup[name] ~= nil then
if(token) then
logger:warn(generateWarning(token, "the variable \"" .. name .. "\" is already defined in that scope"));
else
logger:warn(string.format("a variable with the name \"%s\" was already defined", name));
end
--return self.variablesLookup[name];
end
table.insert(self.variables, name);
local id = #self.variables;
return id;
end
function Scope:addIfNotExists(id)
if(not self.variables[id]) then
local name = string.format("%s%i", config.IdentPrefix, next_name_i);
next_name_i = next_name_i + 1;
self.variables[id] = name;
self.variablesLookup[name] = id;
end
return id;
end
-- Returns wether the variable is defined in this Scope
function Scope:hasVariable(name)
if(self.isGlobal) then
if self.variablesLookup[name] == nil then
self:addVariable(name);
end
return true;
end
return self.variablesLookup[name] ~= nil;
end
-- Get List of all Variables defined in this Scope
function Scope:getVariables()
return self.variables;
end
function Scope:resetReferences(id)
self.referenceCounts[id] = 0;
end
function Scope:getReferences(id)
return self.referenceCounts[id] or 0;
end
function Scope:removeReference(id)
self.referenceCounts[id] = (self.referenceCounts[id] or 0) - 1;
end
function Scope:addReference(id)
self.referenceCounts[id] = (self.referenceCounts[id] or 0) + 1;
end
-- Resolve the scope of a variable by name
function Scope:resolve(name)
if(self:hasVariable(name)) then
return self, self.variablesLookup[name];
end
assert(self.parentScope, "No Global Variable Scope was Created! This should not be Possible!");
local scope, id = self.parentScope:resolve(name);
self:addReferenceToHigherScope(scope, id, nil, true);
return scope, id;
end
function Scope:resolveGlobal(name)
if(self.isGlobal and self:hasVariable(name)) then
return self, self.variablesLookup[name];
end
assert(self.parentScope, "No Global Variable Scope was Created! This should not be Possible!");
local scope, id = self.parentScope:resolveGlobal(name);
self:addReferenceToHigherScope(scope, id, nil, true);
return scope, id;
end
-- Returns the name of an Variable by id - this is used for unparsing
function Scope:getVariableName(id)
return self.variables[id];
end
-- Remove A Variable from this Scope
function Scope:removeVariable(id)
local name = self.variables[id];
self.variables[id] = nil;
self.variablesLookup[name] = nil;
self.skipIdLookup[id] = true;
end
-- Add a Children Scope
function Scope:addChild(scope)
-- This will add all References from that Scope to higher Scopes. Note that the higher scopes may only be global
for scope, ids in pairs(scope.variablesFromHigherScopes) do
for id, count in pairs(ids) do
if count and count > 0 then
self:addReferenceToHigherScope(scope, id, count);
end
end
end
table.insert(self.children, scope);
end
function Scope:clearReferences()
self.referenceCounts = {};
self.variablesFromHigherScopes = {};
end
function Scope:removeChild(child)
for i, v in ipairs(self.children) do
if(v == child) then
-- This will add all References from that Scope to higher Scopes. Note that the higher scopes may only be global
for scope, ids in pairs(v.variablesFromHigherScopes) do
for id, count in pairs(ids) do
if count and count > 0 then
self:removeReferenceToHigherScope(scope, id, count);
end
end
end
return table.remove(self.children, i);
end
end
end
function Scope:getMaxId()
return #self.variables;
end
function Scope:addReferenceToHigherScope(scope, id, n, b)
n = n or 1;
if self.isGlobal then
if not scope.isGlobal then
logger:error(string.format("Could not resolve Scope \"%s\"", scope.name))
end
return
end
if scope == self then
self.referenceCounts[id] = (self.referenceCounts[id] or 0) + n;
return
end
if not self.variablesFromHigherScopes[scope] then
self.variablesFromHigherScopes[scope] = {};
end
local scopeReferences = self.variablesFromHigherScopes[scope];
if scopeReferences[id] then
scopeReferences[id] = scopeReferences[id] + n;
else
scopeReferences[id] = n;
end
if not b then
self.parentScope:addReferenceToHigherScope(scope, id, n);
end
end
function Scope:removeReferenceToHigherScope(scope, id, n, b)
n = n or 1;
if self.isGlobal then
return
end
if scope == self then
self.referenceCounts[id] = (self.referenceCounts[id] or 0) - n;
return
end
if not self.variablesFromHigherScopes[scope] then
self.variablesFromHigherScopes[scope] = {};
end
local scopeReferences = self.variablesFromHigherScopes[scope];
if scopeReferences[id] then
scopeReferences[id] = scopeReferences[id] - n;
else
scopeReferences[id] = 0;
end
if not b then
self.parentScope:removeReferenceToHigherScope(scope, id, n);
end
end
-- Rename Variables from that scope downwards
-- this function needs a settings object with the following properties
-- Keywords => forbidden Variable Names
-- generateName(id, scope, originalName) => function to generate unique variable name based on the id and scope
function Scope:renameVariables(settings)
if(not self.isGlobal) then
local prefix = settings.prefix or "";
local forbiddenNamesLookup = {};
for _, keyword in pairs(settings.Keywords) do
forbiddenNamesLookup[keyword] = true;
end
for scope, ids in pairs(self.variablesFromHigherScopes) do
for id, count in pairs(ids) do
if count and count > 0 then
local name = scope:getVariableName(id);
forbiddenNamesLookup[name] = true;
end
end
end
self.variablesLookup = {};
local i = 0;
for id, originalName in pairs(self.variables) do
if(not self.skipIdLookup[id] and (self.referenceCounts[id] or 0) >= 0) then
local name;
repeat
name = prefix .. settings.generateName(i, self, originalName);
if name == nil then
name = originalName;
end
i = i + 1;
until not forbiddenNamesLookup[name];
self.variables[id] = name;
self.variablesLookup[name] = id;
end
end
end
for _, scope in pairs(self.children) do
scope:renameVariables(settings);
end
end
return Scope;
================================================
FILE: src/prometheus/step.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- step.lua
--
-- This Script provides the base class for Obfuscation Steps
local logger = require("logger");
local util = require("prometheus.util");
local lookupify = util.lookupify;
local Step = {};
Step.SettingsDescriptor = {}
function Step:new(settings)
local instance = {};
setmetatable(instance, self);
self.__index = self;
if type(settings) ~= "table" then
settings = {};
end
for key, data in pairs(self.SettingsDescriptor) do
if settings[key] == nil then
if data.default == nil then
logger:error(string.format("The Setting \"%s\" was not provided for the Step \"%s\"", key, self.Name));
end
instance[key] = data.default;
elseif(data.type == "enum") then
local lookup = lookupify(data.values);
if not lookup[settings[key]] then
logger:error(string.format("Invalid value for the Setting \"%s\" of the Step \"%s\". It must be one of the following: %s", key, self.Name, table.concat(data, ", ")));
end
instance[key] = settings[key];
elseif(type(settings[key]) ~= data.type) then
logger:error(string.format("Invalid value for the Setting \"%s\" of the Step \"%s\". It must be a %s", key, self.Name, data.type));
else
if data.min then
if settings[key] < data.min then
logger:error(string.format("Invalid value for the Setting \"%s\" of the Step \"%s\". It must be at least %d", key, self.Name, data.min));
end
end
if data.max then
if settings[key] > data.max then
logger:error(string.format("Invalid value for the Setting \"%s\" of the Step \"%s\". The biggest allowed value is %d", key, self.Name, data.min));
end
end
instance[key] = settings[key];
end
end
instance:init();
return instance;
end
function Step:init()
logger:error("Abstract Steps cannot be Created");
end
function Step:extend()
local ext = {};
setmetatable(ext, self);
self.__index = self;
return ext;
end
function Step:apply(ast, pipeline)
logger:error("Abstract Steps cannot be Applied")
end
Step.Name = "Abstract Step";
Step.Description = "Abstract Step";
return Step;
================================================
FILE: src/prometheus/steps/AddVararg.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- AddVararg.lua
--
-- This Script provides a Simple Obfuscation Step that wraps the entire Script into a function
local Step = require("prometheus.step");
local Ast = require("prometheus.ast");
local visitast = require("prometheus.visitast");
local AstKind = Ast.AstKind;
local AddVararg = Step:extend();
AddVararg.Description = "This Step Adds Vararg to all Functions";
AddVararg.Name = "Add Vararg";
AddVararg.SettingsDescriptor = {}
function AddVararg:init(_) end
function AddVararg:apply(ast)
visitast(ast, nil, function(node)
if node.kind == AstKind.FunctionDeclaration or node.kind == AstKind.LocalFunctionDeclaration or node.kind == AstKind.FunctionLiteralExpression then
if #node.args < 1 or node.args[#node.args].kind ~= AstKind.VarargExpression then
node.args[#node.args + 1] = Ast.VarargExpression();
end
end
end)
end
return AddVararg;
================================================
FILE: src/prometheus/steps/AntiTamper.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- AntiTamper.lua
--
-- This Script provides an Obfuscation Step, that breaks the script, when someone tries to tamper with it.
local Step = require("prometheus.step")
local RandomStrings = require("prometheus.randomStrings")
local Parser = require("prometheus.parser")
local Enums = require("prometheus.enums")
local logger = require("logger")
local AntiTamper = Step:extend()
AntiTamper.Description = "This Step Breaks your Script when it is modified. This is only effective when using the new VM."
AntiTamper.Name = "Anti Tamper"
AntiTamper.SettingsDescriptor = {
UseDebug = {
type = "boolean",
default = true,
description = "Use debug library. (Recommended, however scripts will not work without debug library.)",
},
}
local function generateSanityCheck()
local sanityCheckAnswers = {}
local sanityPasses = math.random(1, 10)
for i = 1, sanityPasses do
sanityCheckAnswers[i] = (math.random(1, 2 ^ 24) % 2 == 1)
end
local primaryCheck = RandomStrings.randomString()
local codeParts = {}
local function addCode(fmt, ...)
table.insert(codeParts, string.format(fmt, ...))
end
local function generateAssignment(idx)
local index = math.min(idx, sanityPasses)
addCode(" valid = %s;\n", tostring(sanityCheckAnswers[index]))
end
local function generateValidation(idx)
local index = math.min(idx - 1, sanityPasses)
addCode(" if valid == %s then\n", tostring(sanityCheckAnswers[index]))
addCode(" else\n")
addCode(" while true do end\n")
addCode(" end\n")
end
addCode("do local valid = '%s';", primaryCheck)
addCode("for i = 0, %d do\n", sanityPasses)
for i = 0, sanityPasses do
if i == 0 then
addCode(" if i == 0 then\n")
addCode(" if valid ~= '%s' then\n", primaryCheck)
addCode(" while true do end\n")
addCode(" end\n")
addCode(" valid = %s;\n", tostring(sanityCheckAnswers[1]))
elseif i == 1 then
addCode(" elseif i == 1 then\n")
addCode(" if valid == %s then\n", tostring(sanityCheckAnswers[1]))
addCode(" end\n")
else
addCode(" elseif i == %d then\n", i)
--[[
Basically, even iterations are used to assign a new sanity check value,
and odd iterations are used to validate the previous sanity check value.
]]
if i % 2 == 0 then
generateAssignment(i)
else
generateValidation(i)
end
end
end
addCode(" end\n")
addCode(" end\n")
addCode("do valid = true end\n")
return table.concat(codeParts)
end
function AntiTamper:init(settings) end
function AntiTamper:apply(ast, pipeline)
if pipeline.PrettyPrint then
logger:warn(string.format('"%s" cannot be used with PrettyPrint, ignoring "%s"', self.Name, self.Name))
return ast
end
local code = generateSanityCheck()
if self.UseDebug then
local string = RandomStrings.randomString()
code = code
.. [[
-- Anti Beautify
local sethook = debug and debug.sethook or function() end;
local allowedLine = nil;
local called = 0;
sethook(function(s, line)
if not line then
return
end
called = called + 1;
if allowedLine then
if allowedLine ~= line then
sethook(error, "l", 5);
end
else
allowedLine = line;
end
end, "l", 5);
(function() end)();
(function() end)();
sethook();
if called < 2 then
valid = false;
end
if called < 2 then
valid = false;
end
-- Anti Function Hook
local funcs = {pcall, string.char, debug.getinfo, string.dump}
for i = 1, #funcs do
if debug.getinfo(funcs[i]).what ~= "C" then
valid = false;
end
if debug.getupvalue(funcs[i], 1) then
valid = false;
end
if pcall(string.dump, funcs[i]) then
valid = false;
end
end
-- Anti Beautify
local function getTraceback()
local str = (function(arg)
return debug.traceback(arg)
end)("]] .. string .. [[");
return str;
end
local traceback = getTraceback();
valid = valid and traceback:sub(1, traceback:find("\n") - 1) == "]] .. string .. [[";
local iter = traceback:gmatch(":(%d*):");
local v, c = iter(), 1;
for i in iter do
valid = valid and i == v;
c = c + 1;
end
valid = valid and c >= 2;
]]
end
code = code .. [[
local gmatch = string.gmatch;
local err = function() error("Tamper Detected!") end;
local pcallIntact2 = false;
local pcallIntact = pcall(function()
pcallIntact2 = true;
end) and pcallIntact2;
local random = math.random;
local tblconcat = table.concat;
local unpkg = table and table.unpack or unpack;
local n = random(3, 65);
local acc1 = 0;
local acc2 = 0;
local pcallRet = {pcall(function() local a = ]] .. tostring(math.random(1, 2^24)) .. [[ - "]] .. RandomStrings.randomString() .. [[" ^ ]] .. tostring(math.random(1, 2^24)) .. [[ return "]] .. RandomStrings.randomString() .. [[" / a; end)};
local origMsg = pcallRet[2];
local line = tonumber(gmatch(tostring(origMsg), ':(%d*):')());
for i = 1, n do
local len = math.random(1, 100);
local n2 = random(0, 255);
local pos = random(1, len);
local shouldErr = random(1, 2) == 1;
local msg = origMsg:gsub(':(%d*):', ':' .. tostring(random(0, 10000)) .. ':');
local arr = {pcall(function()
if random(1, 2) == 1 or i == n then
local line2 = tonumber(gmatch(tostring(({pcall(function() local a = ]] .. tostring(math.random(1, 2^24)) .. [[ - "]] .. RandomStrings.randomString() .. [[" ^ ]] .. tostring(math.random(1, 2^24)) .. [[ return "]] .. RandomStrings.randomString() .. [[" / a; end)})[2]), ':(%d*):')());
valid = valid and line == line2;
end
if shouldErr then
error(msg, 0);
end
local arr = {};
for i = 1, len do
arr[i] = random(0, 255);
end
arr[pos] = n2;
return unpkg(arr);
end)};
if shouldErr then
valid = valid and arr[1] == false and arr[2] == msg;
else
valid = valid and arr[1];
acc1 = (acc1 + arr[pos + 1]) % 256;
acc2 = (acc2 + n2) % 256;
end
end
valid = valid and acc1 == acc2;
if valid then else
repeat
return (function()
while true do
l1, l2 = l2, l1;
err();
end
end)();
until true;
while true do
l2 = random(1, 6);
if l2 > 2 then
l2 = tostring(l1);
else
l1 = l2;
end
end
return;
end
end
-- Anti Function Arg Hook
local obj = setmetatable({}, {
__tostring = err,
});
obj[math.random(1, 100)] = obj;
(function() end)(obj);
repeat until valid;
]]
local parsed = Parser:new({LuaVersion = Enums.LuaVersion.Lua51}):parse(code);
local doStat = parsed.body.statements[1];
doStat.body.scope:setParent(ast.body.scope);
table.insert(ast.body.statements, 1, doStat);
return ast;
end
return AntiTamper;
================================================
FILE: src/prometheus/steps/ConstantArray.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- ConstantArray.lua
--
-- This Script provides a Simple Obfuscation Step that wraps the entire Script into a function
-- TODO: Wrapper Functions
-- TODO: Proxy Object for indexing: e.g: ARR[X] becomes ARR + X
local Step = require("prometheus.step");
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local visitast = require("prometheus.visitast");
local util = require("prometheus.util")
local Parser = require("prometheus.parser");
local enums = require("prometheus.enums")
local LuaVersion = enums.LuaVersion;
local AstKind = Ast.AstKind;
local ConstantArray = Step:extend();
ConstantArray.Description = "This Step will Extract all Constants and put them into an Array at the beginning of the script";
ConstantArray.Name = "Constant Array";
ConstantArray.SettingsDescriptor = {
Treshold = {
name = "Treshold",
description = "The relative amount of nodes that will be affected",
type = "number",
default = 1,
min = 0,
max = 1,
},
StringsOnly = {
name = "StringsOnly",
description = "Wether to only Extract Strings",
type = "boolean",
default = false,
},
Shuffle = {
name = "Shuffle",
description = "Wether to shuffle the order of Elements in the Array",
type = "boolean",
default = true,
},
Rotate = {
name = "Rotate",
description = "Wether to rotate the String Array by a specific (random) amount. This will be undone on runtime.",
type = "boolean",
default = true,
},
LocalWrapperTreshold = {
name = "LocalWrapperTreshold",
description = "The relative amount of nodes functions, that will get local wrappers",
type = "number",
default = 1,
min = 0,
max = 1,
},
LocalWrapperCount = {
name = "LocalWrapperCount",
description = "The number of Local wrapper Functions per scope. This only applies if LocalWrapperTreshold is greater than 0",
type = "number",
min = 0,
max = 512,
default = 0,
},
LocalWrapperArgCount = {
name = "LocalWrapperArgCount",
description = "The number of Arguments to the Local wrapper Functions",
type = "number",
min = 1,
default = 10,
max = 200,
};
MaxWrapperOffset = {
name = "MaxWrapperOffset",
description = "The Max Offset for the Wrapper Functions",
type = "number",
min = 0,
default = 65535,
};
Encoding = {
name = "Encoding",
description = "The Encoding to use for the Strings",
type = "enum",
default = "mixed",
values = {
"none",
"base64",
"base85",
"mixed",
},
}
}
local prefix_0, prefix_1;
local function initPrefixes()
local charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@£$%^&*()_+-=[]{}|:;<>,./?";
repeat
local a, b = math.random(1, #charset), math.random(1, #charset);
prefix_0 = charset:sub(a, a);
prefix_1 = charset:sub(b, b);
until prefix_0 ~= prefix_1
end
local function callNameGenerator(generatorFunction, ...)
if(type(generatorFunction) == "table") then
generatorFunction = generatorFunction.generateName;
end
return generatorFunction(...);
end
function ConstantArray:init(_) end
function ConstantArray:createArray()
local entries = {};
for i, v in ipairs(self.constants) do
if type(v) == "string" then
v = self:encode(v);
end
entries[i] = Ast.TableEntry(Ast.ConstantNode(v));
end
return Ast.TableConstructorExpression(entries);
end
function ConstantArray:indexing(index, data)
if self.LocalWrapperCount > 0 and data.functionData.local_wrappers then
local wrappers = data.functionData.local_wrappers;
local wrapper = wrappers[math.random(#wrappers)];
local args = {};
local ofs = index - self.wrapperOffset - wrapper.offset;
for i = 1, self.LocalWrapperArgCount, 1 do
if i == wrapper.arg then
args[i] = Ast.NumberExpression(ofs);
else
args[i] = Ast.NumberExpression(math.random(ofs - 1024, ofs + 1024));
end
end
data.scope:addReferenceToHigherScope(wrappers.scope, wrappers.id);
return Ast.FunctionCallExpression(Ast.IndexExpression(
Ast.VariableExpression(wrappers.scope, wrappers.id),
Ast.StringExpression(wrapper.index)
), args);
else
data.scope:addReferenceToHigherScope(self.rootScope, self.wrapperId);
return Ast.FunctionCallExpression(Ast.VariableExpression(self.rootScope, self.wrapperId), {
Ast.NumberExpression(index - self.wrapperOffset);
});
end
end
function ConstantArray:getConstant(value, data)
if(self.lookup[value]) then
return self:indexing(self.lookup[value], data)
end
local idx = #self.constants + 1;
self.constants[idx] = value;
self.lookup[value] = idx;
return self:indexing(idx, data);
end
function ConstantArray:addConstant(value)
if(self.lookup[value]) then
return
end
local idx = #self.constants + 1;
self.constants[idx] = value;
self.lookup[value] = idx;
end
local function reverse(t, i, j)
while i < j do
t[i], t[j] = t[j], t[i]
i, j = i+1, j-1
end
end
local function rotate(t, d, n)
n = n or #t
d = (d or 1) % n
reverse(t, 1, n)
reverse(t, 1, d)
reverse(t, d+1, n)
end
local rotateCode = [=[
for i, v in ipairs({{1, LEN}, {1, SHIFT}, {SHIFT + 1, LEN}}) do
while v[1] < v[2] do
ARR[v[1]], ARR[v[2]], v[1], v[2] = ARR[v[2]], ARR[v[1]], v[1] + 1, v[2] - 1
end
end
]=];
function ConstantArray:addRotateCode(ast, shift)
local parser = Parser:new({
LuaVersion = LuaVersion.Lua51;
});
local newAst = parser:parse(string.gsub(string.gsub(rotateCode, "SHIFT", tostring(shift)), "LEN", tostring(#self.constants)));
local forStat = newAst.body.statements[1];
forStat.body.scope:setParent(ast.body.scope);
visitast(newAst, nil, function(node, data)
if(node.kind == AstKind.VariableExpression) then
if(node.scope:getVariableName(node.id) == "ARR") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
data.scope:addReferenceToHigherScope(self.rootScope, self.arrId);
node.scope = self.rootScope;
node.id = self.arrId;
end
end
end)
table.insert(ast.body.statements, 1, forStat);
end
function ConstantArray:addDecodeCode(ast)
if self.Encoding == "base64" then
local base64DecodeCode = [[
do ]] .. table.concat(util.shuffle{
"local lookup = LOOKUP_TABLE;",
"local len = string.len;",
"local sub = string.sub;",
"local floor = math.floor;",
"local strchar = string.char;",
"local insert = table.insert;",
"local concat = table.concat;",
"local type = type;",
"local arr = ARR;",
}) .. [[
for i = 1, #arr do
local data = arr[i];
if type(data) == "string" then
local length = len(data)
local parts = {}
local index = 1
local value = 0
local count = 0
while index <= length do
local char = sub(data, index, index)
local code = lookup[char]
if code then
value = value + code * (64 ^ (3 - count))
count = count + 1
if count == 4 then
count = 0
local c1 = floor(value / 65536)
local c2 = floor(value % 65536 / 256)
local c3 = value % 256
insert(parts, strchar(c1, c2, c3))
value = 0
end
elseif char == "=" then
insert(parts, strchar(floor(value / 65536)));
if index >= length or sub(data, index + 1, index + 1) ~= "=" then
insert(parts, strchar(floor(value % 65536 / 256)));
end
break
end
index = index + 1
end
arr[i] = concat(parts)
end
end
end
]];
local parser = Parser:new({
LuaVersion = LuaVersion.Lua51;
});
local newAst = parser:parse(base64DecodeCode);
local forStat = newAst.body.statements[1];
forStat.body.scope:setParent(ast.body.scope);
visitast(newAst, nil, function(node, data)
if(node.kind == AstKind.VariableExpression) then
if(node.scope:getVariableName(node.id) == "ARR") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
data.scope:addReferenceToHigherScope(self.rootScope, self.arrId);
node.scope = self.rootScope;
node.id = self.arrId;
end
if(node.scope:getVariableName(node.id) == "LOOKUP_TABLE") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
return self:createBase64Lookup();
end
end
end)
table.insert(ast.body.statements, 1, forStat);
elseif self.Encoding == "base85" then
local base85DecodeCode = [[
do ]] .. table.concat(util.shuffle{
"local lookup = LOOKUP_TABLE;",
"local len = string.len;",
"local sub = string.sub;",
"local floor = math.floor;",
"local strchar = string.char;",
"local insert = table.insert;",
"local concat = table.concat;",
"local type = type;",
"local arr = ARR;",
}) .. [[
for i = 1, #arr do
local data = arr[i];
if type(data) == "string" then
local length = len(data)
local parts = {}
local index = 1
while index <= length do
local remain = length - index + 1
local count = remain >= 5 and 5 or remain
local value = 0
local valid = count > 1
for j = 0, 4 do
local code
if j < count then
local ch = sub(data, index + j, index + j)
code = lookup[ch]
if not code then
valid = false
break
end
else
code = 84
end
value = value * 85 + code
end
if valid then
local b1 = floor(value / 16777216) % 256
local b2 = floor(value / 65536) % 256
local b3 = floor(value / 256) % 256
local b4 = value % 256
if count == 5 then
insert(parts, strchar(b1, b2, b3, b4))
elseif count == 4 then
insert(parts, strchar(b1, b2, b3))
elseif count == 3 then
insert(parts, strchar(b1, b2))
elseif count == 2 then
insert(parts, strchar(b1))
end
end
index = index + count
end
arr[i] = concat(parts)
end
end
end
]];
local parser = Parser:new({
LuaVersion = LuaVersion.Lua51;
});
local newAst = parser:parse(base85DecodeCode);
local forStat = newAst.body.statements[1];
forStat.body.scope:setParent(ast.body.scope);
visitast(newAst, nil, function(node, data)
if(node.kind == AstKind.VariableExpression) then
if(node.scope:getVariableName(node.id) == "ARR") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
data.scope:addReferenceToHigherScope(self.rootScope, self.arrId);
node.scope = self.rootScope;
node.id = self.arrId;
end
if(node.scope:getVariableName(node.id) == "LOOKUP_TABLE") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
return self:createBase85Lookup();
end
end
end)
table.insert(ast.body.statements, 1, forStat);
elseif self.Encoding == "mixed" then
local mixedDecodeCode = [[
do ]] .. table.concat(util.shuffle{
"local lookup64 = LOOKUP_TABLE_64;",
"local lookup85 = LOOKUP_TABLE_85;",
"local len = string.len;",
"local sub = string.sub;",
"local floor = math.floor;",
"local strchar = string.char;",
"local insert = table.insert;",
"local concat = table.concat;",
"local type = type;",
"local arr = ARR;",
}) .. [[
for i = 1, #arr do
local data = arr[i];
if type(data) == "string" then
local first = sub(data, 1, 1)
if first == "]]..prefix_0..[[" then
data = sub(data, 2)
local length = len(data)
local parts = {}
local index = 1
local value = 0
local count = 0
while index <= length do
local char = sub(data, index, index)
local code = lookup64[char]
if code then
value = value + code * (64 ^ (3 - count))
count = count + 1
if count == 4 then
count = 0
local c1 = floor(value / 65536)
local c2 = floor(value % 65536 / 256)
local c3 = value % 256
insert(parts, strchar(c1, c2, c3))
value = 0
end
elseif char == "=" then
insert(parts, strchar(floor(value / 65536)));
if index >= length or sub(data, index + 1, index + 1) ~= "=" then
insert(parts, strchar(floor(value % 65536 / 256)));
end
break
end
index = index + 1
end
arr[i] = concat(parts)
elseif first == "]]..prefix_1..[[" then
data = sub(data, 2)
local length = len(data)
local parts = {}
local idx = 1
while idx <= length do
local remain = length - idx + 1
local count = remain >= 5 and 5 or remain
local value = 0
local valid = count > 1
for j = 0, 4 do
local code
if j < count then
local ch = sub(data, idx + j, idx + j)
code = lookup85[ch]
if not code then
valid = false
break
end
else
code = 84
end
value = value * 85 + code
end
if valid then
local b1 = floor(value / 16777216) % 256
local b2 = floor(value / 65536) % 256
local b3 = floor(value / 256) % 256
local b4 = value % 256
if count == 5 then
insert(parts, strchar(b1, b2, b3, b4))
elseif count == 4 then
insert(parts, strchar(b1, b2, b3))
elseif count == 3 then
insert(parts, strchar(b1, b2))
elseif count == 2 then
insert(parts, strchar(b1))
end
end
idx = idx + count
end
arr[i] = concat(parts)
end
end
end
end
]];
local parser = Parser:new({
LuaVersion = LuaVersion.Lua51;
});
local newAst = parser:parse(mixedDecodeCode);
local forStat = newAst.body.statements[1];
forStat.body.scope:setParent(ast.body.scope);
visitast(newAst, nil, function(node, data)
if(node.kind == AstKind.VariableExpression) then
if(node.scope:getVariableName(node.id) == "ARR") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
data.scope:addReferenceToHigherScope(self.rootScope, self.arrId);
node.scope = self.rootScope;
node.id = self.arrId;
end
if(node.scope:getVariableName(node.id) == "LOOKUP_TABLE_64") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
return self:createBase64Lookup();
end
if(node.scope:getVariableName(node.id) == "LOOKUP_TABLE_85") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
return self:createBase85Lookup();
end
end
end)
table.insert(ast.body.statements, 1, forStat);
end
end
function ConstantArray:createBase64Lookup()
local entries = {};
local i = 0;
for char in string.gmatch(self.base64chars, ".") do
table.insert(entries, Ast.KeyedTableEntry(Ast.StringExpression(char), Ast.NumberExpression(i)));
i = i + 1;
end
util.shuffle(entries);
return Ast.TableConstructorExpression(entries);
end
function ConstantArray:createBase85Lookup()
local entries = {};
local i = 0;
for char in string.gmatch(self.base85chars, ".") do
table.insert(entries, Ast.KeyedTableEntry(Ast.StringExpression(char), Ast.NumberExpression(i)));
i = i + 1;
end
util.shuffle(entries);
return Ast.TableConstructorExpression(entries);
end
function ConstantArray:encode(str)
if self.Encoding == "base64" then
return ((str:gsub('.', function(x)
local r,b='',x:byte()
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return self.base64chars:sub(c+1,c+1)
end)..({ '', '==', '=' })[#str%3+1]);
elseif self.Encoding == "base85" then
local result = {};
local len = #str;
local pos = 1;
while pos <= len do
local rem = len - pos + 1;
local count = rem >= 4 and 4 or rem;
local b1, b2, b3, b4 = string.byte(str, pos, pos + count - 1);
b1, b2, b3, b4 = b1 or 0, b2 or 0, b3 or 0, b4 or 0;
local value = ((b1 * 256 + b2) * 256 + b3) * 256 + b4;
local chars = {};
for i = 5, 1, -1 do
local code = (value % 85) + 1;
chars[i] = self.base85chars:sub(code, code);
value = math.floor(value / 85);
end
result[#result + 1] = table.concat(chars, "", 1, count + 1);
pos = pos + count;
end
return table.concat(result);
elseif self.Encoding == "mixed" then
if math.random() < 0.5 then
local encoded = ((str:gsub('.', function(x)
local r,b='',x:byte()
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return self.base64chars:sub(c+1,c+1)
end)..({ '', '==', '=' })[#str%3+1]);
return prefix_0 .. encoded;
else
local result = {};
local len = #str;
local pos = 1;
while pos <= len do
local rem = len - pos + 1;
local count = rem >= 4 and 4 or rem;
local b1, b2, b3, b4 = string.byte(str, pos, pos + count - 1);
b1 = b1 or 0;
b2 = b2 or 0;
b3 = b3 or 0;
b4 = b4 or 0;
local value = ((b1 * 256 + b2) * 256 + b3) * 256 + b4;
local chars = {};
for i = 5, 1, -1 do
local code = (value % 85) + 1;
chars[i] = self.base85chars:sub(code, code);
value = math.floor(value / 85);
end
result[#result + 1] = table.concat(chars, "", 1, count + 1);
pos = pos + count;
end
return prefix_1 .. table.concat(result);
end
end
end
function ConstantArray:apply(ast, pipeline)
initPrefixes();
self.rootScope = ast.body.scope;
self.arrId = self.rootScope:addVariable();
self.base64chars = table.concat(util.shuffle{
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"+", "/",
});
self.base85chars = table.concat(util.shuffle{
"!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
":", ";", "<", "=", ">", "?", "@",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"[", "\\", "]", "^", "_", "`",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u",
});
self.constants = {};
self.lookup = {};
-- Extract Constants
visitast(ast, nil, function(node, data)
-- Apply only to some nodes
if math.random() <= self.Treshold then
node.__apply_constant_array = true;
if node.kind == AstKind.StringExpression then
self:addConstant(node.value);
elseif not self.StringsOnly then
if node.isConstant then
if node.value ~= nil then
self:addConstant(node.value);
end
end
end
end
end);
-- Shuffle Array
if self.Shuffle then
self.constants = util.shuffle(self.constants);
self.lookup = {};
for i, v in ipairs(self.constants) do
self.lookup[v] = i;
end
end
-- Set Wrapper Function Offset
self.wrapperOffset = math.random(-self.MaxWrapperOffset, self.MaxWrapperOffset);
self.wrapperId = self.rootScope:addVariable();
visitast(ast, function(node, data)
-- Add Local Wrapper Functions
if self.LocalWrapperCount > 0 and node.kind == AstKind.Block and node.isFunctionBlock and math.random() <= self.LocalWrapperTreshold then
local id = node.scope:addVariable()
data.functionData.local_wrappers = {
id = id;
scope = node.scope,
};
local nameLookup = {};
for i = 1, self.LocalWrapperCount, 1 do
local name;
repeat
name = callNameGenerator(pipeline.namegenerator, math.random(1, self.LocalWrapperArgCount * 16));
until not nameLookup[name];
nameLookup[name] = true;
local offset = math.random(-self.MaxWrapperOffset, self.MaxWrapperOffset);
local argPos = math.random(1, self.LocalWrapperArgCount);
data.functionData.local_wrappers[i] = {
arg = argPos,
index = name,
offset = offset,
};
data.functionData.__used = false;
end
end
if node.__apply_constant_array then
data.functionData.__used = true;
end
end, function(node, data)
-- Actually insert Statements to get the Constant Values
if node.__apply_constant_array then
if node.kind == AstKind.StringExpression then
return self:getConstant(node.value, data);
elseif not self.StringsOnly then
if node.isConstant then
return node.value ~= nil and self:getConstant(node.value, data);
end
end
node.__apply_constant_array = nil;
end
-- Insert Local Wrapper Declarations
if self.LocalWrapperCount > 0 and node.kind == AstKind.Block and node.isFunctionBlock and data.functionData.local_wrappers and data.functionData.__used then
data.functionData.__used = nil;
local elems = {};
local wrappers = data.functionData.local_wrappers;
for i = 1, self.LocalWrapperCount, 1 do
local wrapper = wrappers[i];
local argPos = wrapper.arg;
local offset = wrapper.offset;
local name = wrapper.index;
local funcScope = Scope:new(node.scope);
local arg = nil;
local args = {};
for i = 1, self.LocalWrapperArgCount, 1 do
args[i] = funcScope:addVariable();
if i == argPos then
arg = args[i];
end
end
local addSubArg;
-- Create add and Subtract code
if offset < 0 then
addSubArg = Ast.SubExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(-offset));
else
addSubArg = Ast.AddExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(offset));
end
funcScope:addReferenceToHigherScope(self.rootScope, self.wrapperId);
local callArg = Ast.FunctionCallExpression(Ast.VariableExpression(self.rootScope, self.wrapperId), {
addSubArg
});
local fargs = {};
for i, v in ipairs(args) do
fargs[i] = Ast.VariableExpression(funcScope, v);
end
elems[i] = Ast.KeyedTableEntry(
Ast.StringExpression(name),
Ast.FunctionLiteralExpression(fargs, Ast.Block({
Ast.ReturnStatement({
callArg
});
}, funcScope))
)
end
table.insert(node.statements, 1, Ast.LocalVariableDeclaration(node.scope, {
wrappers.id
}, {
Ast.TableConstructorExpression(elems)
}));
end
end);
self:addDecodeCode(ast);
local steps = util.shuffle({
-- Add Wrapper Function Code
function()
local funcScope = Scope:new(self.rootScope);
-- Add Reference to Array
funcScope:addReferenceToHigherScope(self.rootScope, self.arrId);
local arg = funcScope:addVariable();
local addSubArg;
-- Create add and Subtract code
if self.wrapperOffset < 0 then
addSubArg = Ast.SubExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(-self.wrapperOffset));
else
addSubArg = Ast.AddExpression(Ast.VariableExpression(funcScope, arg), Ast.NumberExpression(self.wrapperOffset));
end
-- Create and Add the Function Declaration
table.insert(ast.body.statements, 1, Ast.LocalFunctionDeclaration(self.rootScope, self.wrapperId, {
Ast.VariableExpression(funcScope, arg)
}, Ast.Block({
Ast.ReturnStatement({
Ast.IndexExpression(
Ast.VariableExpression(self.rootScope, self.arrId),
addSubArg
)
});
}, funcScope)));
-- Resulting Code:
-- function xy(a)
-- return ARR[a - 10]
-- end
end,
-- Rotate Array and Add unrotate code
function()
if self.Rotate and #self.constants > 1 then
local shift = math.random(1, #self.constants - 1);
rotate(self.constants, -shift);
self:addRotateCode(ast, shift);
end
end,
});
for i, f in ipairs(steps) do
f();
end
-- Add the Array Declaration
table.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(self.rootScope, {self.arrId}, {self:createArray()}));
self.rootScope = nil;
self.arrId = nil;
self.constants = nil;
self.lookup = nil;
end
return ConstantArray;
================================================
FILE: src/prometheus/steps/EncryptStrings.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- EncryptStrings.lua
--
-- This Script provides a Simple Obfuscation Step that encrypts strings
local Step = require("prometheus.step")
local Ast = require("prometheus.ast")
local Parser = require("prometheus.parser")
local Enums = require("prometheus.enums")
local visitast = require("prometheus.visitast");
local util = require("prometheus.util")
local AstKind = Ast.AstKind;
local EncryptStrings = Step:extend()
EncryptStrings.Description = "This Step will encrypt strings within your Program."
EncryptStrings.Name = "Encrypt Strings"
EncryptStrings.SettingsDescriptor = {}
function EncryptStrings:init(_) end
function EncryptStrings:CreateEncryptionService()
local usedSeeds = {};
local secret_key_6 = math.random(0, 63) -- 6-bit arbitrary integer (0..63)
local secret_key_7 = math.random(0, 127) -- 7-bit arbitrary integer (0..127)
local secret_key_44 = math.random(0, 17592186044415) -- 44-bit arbitrary integer (0..17592186044415)
local secret_key_8 = math.random(0, 255); -- 8-bit arbitrary integer (0..255)
local floor = math.floor
local function primitive_root_257(idx)
local g, m, d = 1, 128, 2 * idx + 1
repeat
g, m, d = g * g * (d >= m and 3 or 1) % 257, m / 2, d % m
until m < 1
return g
end
local param_mul_8 = primitive_root_257(secret_key_7)
local param_mul_45 = secret_key_6 * 4 + 1
local param_add_45 = secret_key_44 * 2 + 1
local state_45 = 0
local state_8 = 2
local prev_values = {}
local function set_seed(seed_53)
state_45 = seed_53 % 35184372088832
state_8 = seed_53 % 255 + 2
prev_values = {}
end
local function gen_seed()
local seed;
repeat
seed = math.random(0, 35184372088832);
until not usedSeeds[seed];
usedSeeds[seed] = true;
return seed;
end
local function get_random_32()
state_45 = (state_45 * param_mul_45 + param_add_45) % 35184372088832
repeat
state_8 = state_8 * param_mul_8 % 257
until state_8 ~= 1
local r = state_8 % 32
local n = floor(state_45 / 2 ^ (13 - (state_8 - r) / 32)) % 2 ^ 32 / 2 ^ r
return floor(n % 1 * 2 ^ 32) + floor(n)
end
local function get_next_pseudo_random_byte()
if #prev_values == 0 then
local rnd = get_random_32() -- value 0..4294967295
local low_16 = rnd % 65536
local high_16 = (rnd - low_16) / 65536
local b1 = low_16 % 256
local b2 = (low_16 - b1) / 256
local b3 = high_16 % 256
local b4 = (high_16 - b3) / 256
prev_values = { b1, b2, b3, b4 }
end
--print(unpack(prev_values))
return table.remove(prev_values)
end
local function encrypt(str)
local seed = gen_seed();
set_seed(seed)
local len = string.len(str)
local out = {}
local prevVal = secret_key_8;
for i = 1, len do
local byte = string.byte(str, i);
out[i] = string.char((byte - (get_next_pseudo_random_byte() + prevVal)) % 256);
prevVal = byte;
end
return table.concat(out), seed;
end
local function genCode()
local code = [[
do
]] .. table.concat(util.shuffle{
"local floor = math.floor",
"local random = math.random",
"local remove = table.remove",
"local char = string.char",
"local state_45 = 0",
"local state_8 = 2",
"local charmap = {}",
"local nums = {}"
}, "\n") .. [[
for i = 1, 256 do
nums[i] = i;
end
repeat
local idx = random(1, #nums);
local n = remove(nums, idx);
charmap[n] = char(n - 1);
until #nums == 0;
local prev_values = {}
local function get_next_pseudo_random_byte()
if #prev_values == 0 then
state_45 = (state_45 * ]] .. tostring(param_mul_45) .. [[ + ]] .. tostring(param_add_45) .. [[) % 35184372088832
repeat
state_8 = state_8 * ]] .. tostring(param_mul_8) .. [[ % 257
until state_8 ~= 1
local r = state_8 % 32
local shift = 13 - (state_8 - r) / 32
local n = floor(state_45 / 2 ^ shift) % 4294967296 / 2 ^ r
local rnd = floor(n % 1 * 4294967296) + floor(n)
local low_16 = rnd % 65536
local high_16 = (rnd - low_16) / 65536
prev_values = { low_16 % 256, (low_16 - low_16 % 256) / 256, high_16 % 256, (high_16 - high_16 % 256) / 256 }
end
local prevValuesLen = #prev_values;
local removed = prev_values[prevValuesLen];
prev_values[prevValuesLen] = nil;
return removed;
end
local realStrings = {};
STRINGS = setmetatable({}, {
__index = realStrings;
__metatable = nil;
});
function DECRYPT(str, seed)
local realStringsLocal = realStrings;
if(realStringsLocal[seed]) then return seed; else
prev_values = {};
local chars = charmap;
state_45 = seed % 35184372088832
state_8 = seed % 255 + 2
local len = #str;
realStringsLocal[seed] = "";
local prevVal = ]] .. tostring(secret_key_8) .. [[;
local s = "";
for i=1, len, 1 do
prevVal = (string.byte(str, i) + get_next_pseudo_random_byte() + prevVal) % 256
s = s .. chars[prevVal + 1];
end
realStringsLocal[seed] = s;
end
return seed;
end
end]]
return code;
end
return {
encrypt = encrypt,
param_mul_45 = param_mul_45,
param_mul_8 = param_mul_8,
param_add_45 = param_add_45,
secret_key_8 = secret_key_8,
genCode = genCode,
}
end
function EncryptStrings:apply(ast, _)
local Encryptor = self:CreateEncryptionService();
local code = Encryptor.genCode();
local newAst = Parser:new({ LuaVersion = Enums.LuaVersion.Lua51 }):parse(code);
local doStat = newAst.body.statements[1];
local scope = ast.body.scope;
local decryptVar = scope:addVariable();
local stringsVar = scope:addVariable();
doStat.body.scope:setParent(ast.body.scope);
visitast(newAst, nil, function(node, data)
if(node.kind == AstKind.FunctionDeclaration) then
if(node.scope:getVariableName(node.id) == "DECRYPT") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
data.scope:addReferenceToHigherScope(scope, decryptVar);
node.scope = scope;
node.id = decryptVar;
end
end
if(node.kind == AstKind.AssignmentVariable or node.kind == AstKind.VariableExpression) then
if(node.scope:getVariableName(node.id) == "STRINGS") then
data.scope:removeReferenceToHigherScope(node.scope, node.id);
data.scope:addReferenceToHigherScope(scope, stringsVar);
node.scope = scope;
node.id = stringsVar;
end
end
end)
visitast(ast, nil, function(node, data)
if(node.kind == AstKind.StringExpression) then
data.scope:addReferenceToHigherScope(scope, stringsVar);
data.scope:addReferenceToHigherScope(scope, decryptVar);
local encrypted, seed = Encryptor.encrypt(node.value);
return Ast.IndexExpression(Ast.VariableExpression(scope, stringsVar), Ast.FunctionCallExpression(Ast.VariableExpression(scope, decryptVar), {
Ast.StringExpression(encrypted), Ast.NumberExpression(seed),
}));
end
end)
-- Insert to Main Ast
table.insert(ast.body.statements, 1, doStat);
table.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(scope, util.shuffle{ decryptVar, stringsVar }, {}));
return ast
end
return EncryptStrings
================================================
FILE: src/prometheus/steps/NumbersToExpressions.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- NumbersToExpressions.lua
--
-- This Script provides an Obfuscation Step, that converts Number Literals to expressions.
-- This step can now also convert numbers to different representations!
-- Supported representations: hex, binary, scientific, normal. Please note that binary is only supported in Lua 5.2 and above.
unpack = unpack or table.unpack
local Step = require("prometheus.step")
local Ast = require("prometheus.ast")
local visitast = require("prometheus.visitast")
local util = require("prometheus.util")
local logger = require("logger")
local AstKind = Ast.AstKind
local NumbersToExpressions = Step:extend()
NumbersToExpressions.Description = "This Step Converts number Literals to Expressions"
NumbersToExpressions.Name = "Numbers To Expressions"
NumbersToExpressions.SettingsDescriptor = {
Threshold = {
type = "number",
default = 1,
min = 0,
max = 1,
},
InternalThreshold = {
type = "number",
default = 0.2,
min = 0,
max = 0.8,
},
NumberRepresentationMutaton = {
type = "boolean",
default = false,
},
AllowedNumberRepresentations = {
type = "table",
default = {"hex", "scientific", "normal"},
values = {"hex", "binary", "scientific", "normal"},
},
}
local function generateModuloExpression(n)
local rhs = n + math.random(1, 2^24)
local multiplier = math.random(1, 2^8)
local lhs = n + (multiplier * rhs)
return lhs, rhs
end
local function contains(table, value)
for _, v in ipairs(table) do
if v == value then
return true
end
end
return false
end
function NumbersToExpressions:init(_)
self.ExpressionGenerators = {
function(val, depth) -- Addition
local val2 = math.random(-2 ^ 20, 2 ^ 20)
local diff = val - val2
if tonumber(tostring(diff)) + tonumber(tostring(val2)) ~= val then
return false
end
return Ast.AddExpression(
self:CreateNumberExpression(val2, depth),
self:CreateNumberExpression(diff, depth),
false
)
end,
function(val, depth) -- Subtraction
local val2 = math.random(-2 ^ 20, 2 ^ 20)
local diff = val + val2
if tonumber(tostring(diff)) - tonumber(tostring(val2)) ~= val then
return false
end
return Ast.SubExpression(
self:CreateNumberExpression(diff, depth),
self:CreateNumberExpression(val2, depth),
false
)
end,
function(val, depth) -- Modulo
local lhs, rhs = generateModuloExpression(val)
if tonumber(tostring(lhs)) % tonumber(tostring(rhs)) ~= val then
return false
end
return Ast.ModExpression(
self:CreateNumberExpression(lhs, depth),
self:CreateNumberExpression(rhs, depth),
false
)
end,
}
end
function NumbersToExpressions:CreateNumberExpression(val, depth)
if depth > 0 and math.random() >= self.InternalThreshold or depth > 15 then
local format = self.AllowedNumberRepresentations[math.random(1, #self.AllowedNumberRepresentations)]
if not self.NumberRepresentationMutaton then
return Ast.NumberExpression(val)
end
if format == "hex" then
if val ~= math.floor(val) or val < 0 then
return Ast.NumberExpression(val)
end
local hexStr = string.format("0x%X", val)
local result = ""
for i = 1, #hexStr do
local c = hexStr:sub(i, i)
if math.random() > 0.5 then
result = result .. c:upper()
else
result = result .. c:lower()
end
end
return Ast.NumberExpression(result)
end
if format == "binary" then
if val ~= math.floor(val) or val < 0 then
return Ast.NumberExpression(val)
end
local binary = ""
local n = val
if n == 0 then
binary = "0"
else
while n > 0 do
binary = (n % 2) .. binary
n = math.floor(n / 2)
end
end
return Ast.NumberExpression("0b" .. binary)
end
if format == "scientific" then
if val == 0 then
return Ast.NumberExpression(val)
end
local exp = math.floor(math.log10(math.abs(val)))
local mantissa = val / (10 ^ exp)
return Ast.NumberExpression(string.format("%.15ge%d", mantissa, exp))
end
if format == "normal" then
return Ast.NumberExpression(val)
end
end
local generators = util.shuffle({ unpack(self.ExpressionGenerators) })
for _, generator in ipairs(generators) do
local node = generator(val, depth + 1)
if node then
return node
end
end
return Ast.NumberExpression(val)
end
function NumbersToExpressions:apply(ast)
if contains(self.AllowedNumberRepresentations, "binary") then
logger:warn("Warning: Binary representation is only supported in Lua 5.2 and above!")
end
visitast(ast, nil, function(node, _)
if node.kind == AstKind.NumberExpression then
if math.random() <= self.Threshold then
return self:CreateNumberExpression(node.value, 0)
end
end
end)
end
return NumbersToExpressions
================================================
FILE: src/prometheus/steps/ProxifyLocals.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- ProxifyLocals.lua
--
-- This Script provides a Obfuscation Step for putting all Locals into Proxy Objects
local Step = require("prometheus.step");
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local visitast = require("prometheus.visitast");
local RandomLiterals = require("prometheus.randomLiterals")
local AstKind = Ast.AstKind;
local ProxifyLocals = Step:extend();
ProxifyLocals.Description = "This Step wraps all locals into Proxy Objects";
ProxifyLocals.Name = "Proxify Locals";
ProxifyLocals.SettingsDescriptor = {
LiteralType = {
name = "LiteralType",
description = "The type of the randomly generated literals",
type = "enum",
values = {
"dictionary",
"number",
"string",
"any",
},
default = "string",
},
}
local function shallowcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
local function callNameGenerator(generatorFunction, ...)
if(type(generatorFunction) == "table") then
generatorFunction = generatorFunction.generateName;
end
return generatorFunction(...);
end
local MetatableExpressions = {
{
constructor = Ast.AddExpression,
key = "__add";
},
{
constructor = Ast.SubExpression,
key = "__sub";
},
{
constructor = Ast.IndexExpression,
key = "__index";
},
{
constructor = Ast.MulExpression,
key = "__mul";
},
{
constructor = Ast.DivExpression,
key = "__div";
},
{
constructor = Ast.PowExpression,
key = "__pow";
},
{
constructor = Ast.StrCatExpression,
key = "__concat";
}
}
function ProxifyLocals:init(_) end
local function generateLocalMetatableInfo(pipeline)
local usedOps = {};
local info = {};
for i, v in ipairs({"setValue", "getValue", "index"}) do
local rop;
repeat
rop = MetatableExpressions[math.random(#MetatableExpressions)];
until not usedOps[rop];
usedOps[rop] = true;
info[v] = rop;
end
info.valueName = callNameGenerator(pipeline.namegenerator, math.random(1, 4096));
return info;
end
function ProxifyLocals:CreateAssignmentExpression(info, expr, parentScope)
local metatableVals = {};
-- Setvalue Entry
local setValueFunctionScope = Scope:new(parentScope);
local setValueSelf = setValueFunctionScope:addVariable();
local setValueArg = setValueFunctionScope:addVariable();
local setvalueFunctionLiteral = Ast.FunctionLiteralExpression(
{
Ast.VariableExpression(setValueFunctionScope, setValueSelf), -- Argument 1
Ast.VariableExpression(setValueFunctionScope, setValueArg), -- Argument 2
},
Ast.Block({ -- Create Function Body
Ast.AssignmentStatement({
Ast.AssignmentIndexing(Ast.VariableExpression(setValueFunctionScope, setValueSelf), Ast.StringExpression(info.valueName));
}, {
Ast.VariableExpression(setValueFunctionScope, setValueArg)
})
}, setValueFunctionScope)
);
table.insert(metatableVals, Ast.KeyedTableEntry(Ast.StringExpression(info.setValue.key), setvalueFunctionLiteral));
-- Getvalue Entry
local getValueFunctionScope = Scope:new(parentScope);
local getValueSelf = getValueFunctionScope:addVariable();
local getValueArg = getValueFunctionScope:addVariable();
local getValueIdxExpr;
if(info.getValue.key == "__index" or info.setValue.key == "__index") then
getValueIdxExpr = Ast.FunctionCallExpression(Ast.VariableExpression(getValueFunctionScope:resolveGlobal("rawget")), {
Ast.VariableExpression(getValueFunctionScope, getValueSelf),
Ast.StringExpression(info.valueName),
});
else
getValueIdxExpr = Ast.IndexExpression(Ast.VariableExpression(getValueFunctionScope, getValueSelf), Ast.StringExpression(info.valueName));
end
local getvalueFunctionLiteral = Ast.FunctionLiteralExpression(
{
Ast.VariableExpression(getValueFunctionScope, getValueSelf), -- Argument 1
Ast.VariableExpression(getValueFunctionScope, getValueArg), -- Argument 2
},
Ast.Block({ -- Create Function Body
Ast.ReturnStatement({
getValueIdxExpr;
});
}, getValueFunctionScope)
);
table.insert(metatableVals, Ast.KeyedTableEntry(Ast.StringExpression(info.getValue.key), getvalueFunctionLiteral));
parentScope:addReferenceToHigherScope(self.setMetatableVarScope, self.setMetatableVarId);
return Ast.FunctionCallExpression(
Ast.VariableExpression(self.setMetatableVarScope, self.setMetatableVarId),
{
Ast.TableConstructorExpression({
Ast.KeyedTableEntry(Ast.StringExpression(info.valueName), expr)
}),
Ast.TableConstructorExpression(metatableVals)
}
);
end
function ProxifyLocals:apply(ast, pipeline)
local localMetatableInfos = {};
local function getLocalMetatableInfo(scope, id)
-- Global Variables should not be transformed
if(scope.isGlobal) then return nil end;
localMetatableInfos[scope] = localMetatableInfos[scope] or {};
if localMetatableInfos[scope][id] then
-- If locked, return no Metatable
if localMetatableInfos[scope][id].locked then
return nil
end
return localMetatableInfos[scope][id];
end
local localMetatableInfo = generateLocalMetatableInfo(pipeline);
localMetatableInfos[scope][id] = localMetatableInfo;
return localMetatableInfo;
end
local function disableMetatableInfo(scope, id)
-- Global Variables should not be transformed
if(scope.isGlobal) then return nil end;
localMetatableInfos[scope] = localMetatableInfos[scope] or {};
localMetatableInfos[scope][id] = {locked = true}
end
-- Create Setmetatable Variable
self.setMetatableVarScope = ast.body.scope;
self.setMetatableVarId = ast.body.scope:addVariable();
-- Create Empty Function Variable
self.emptyFunctionScope = ast.body.scope;
self.emptyFunctionId = ast.body.scope:addVariable();
self.emptyFunctionUsed = false;
-- Add Empty Function Declaration
table.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(self.emptyFunctionScope, {self.emptyFunctionId}, {
Ast.FunctionLiteralExpression({}, Ast.Block({}, Scope:new(ast.body.scope)));
}));
visitast(ast, function(node, data)
-- Lock for loop variables
if(node.kind == AstKind.ForStatement) then
disableMetatableInfo(node.scope, node.id)
end
if(node.kind == AstKind.ForInStatement) then
for i, id in ipairs(node.ids) do
disableMetatableInfo(node.scope, id);
end
end
-- Lock Function Arguments
if(node.kind == AstKind.FunctionDeclaration or node.kind == AstKind.LocalFunctionDeclaration or node.kind == AstKind.FunctionLiteralExpression) then
for i, expr in ipairs(node.args) do
if expr.kind == AstKind.VariableExpression then
disableMetatableInfo(expr.scope, expr.id);
end
end
end
-- Assignment Statements may be Obfuscated Differently
if(node.kind == AstKind.AssignmentStatement) then
if(#node.lhs == 1 and node.lhs[1].kind == AstKind.AssignmentVariable) then
local variable = node.lhs[1];
local localMetatableInfo = getLocalMetatableInfo(variable.scope, variable.id);
if localMetatableInfo then
local args = shallowcopy(node.rhs);
local vexp = Ast.VariableExpression(variable.scope, variable.id);
vexp.__ignoreProxifyLocals = true;
args[1] = localMetatableInfo.setValue.constructor(vexp, args[1]);
self.emptyFunctionUsed = true;
data.scope:addReferenceToHigherScope(self.emptyFunctionScope, self.emptyFunctionId);
return Ast.FunctionCallStatement(Ast.VariableExpression(self.emptyFunctionScope, self.emptyFunctionId), args);
end
end
end
end, function(node, data)
-- Local Variable Declaration
if(node.kind == AstKind.LocalVariableDeclaration) then
for i, id in ipairs(node.ids) do
local expr = node.expressions[i] or Ast.NilExpression();
local localMetatableInfo = getLocalMetatableInfo(node.scope, id);
-- Apply Only to Some Variables if Threshold is non 1
if localMetatableInfo then
local newExpr = self:CreateAssignmentExpression(localMetatableInfo, expr, node.scope);
node.expressions[i] = newExpr;
end
end
end
-- Variable Expression
if(node.kind == AstKind.VariableExpression and not node.__ignoreProxifyLocals) then
local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);
-- Apply Only to Some Variables if Threshold is non 1
if localMetatableInfo then
local literal;
if self.LiteralType == "dictionary" then
literal = RandomLiterals.Dictionary();
elseif self.LiteralType == "number" then
literal = RandomLiterals.Number();
elseif self.LiteralType == "string" then
literal = RandomLiterals.String(pipeline);
else
literal = RandomLiterals.Any(pipeline);
end
return localMetatableInfo.getValue.constructor(node, literal);
end
end
-- Assignment Variable for Assignment Statement
if(node.kind == AstKind.AssignmentVariable) then
local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);
-- Apply Only to Some Variables if Threshold is non 1
if localMetatableInfo then
return Ast.AssignmentIndexing(node, Ast.StringExpression(localMetatableInfo.valueName));
end
end
-- Local Function Declaration
if(node.kind == AstKind.LocalFunctionDeclaration) then
local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);
-- Apply Only to Some Variables if Threshold is non 1
if localMetatableInfo then
local funcLiteral = Ast.FunctionLiteralExpression(node.args, node.body);
local newExpr = self:CreateAssignmentExpression(localMetatableInfo, funcLiteral, node.scope);
return Ast.LocalVariableDeclaration(node.scope, {node.id}, {newExpr});
end
end
-- Function Declaration
if(node.kind == AstKind.FunctionDeclaration) then
local localMetatableInfo = getLocalMetatableInfo(node.scope, node.id);
if(localMetatableInfo) then
table.insert(node.indices, 1, localMetatableInfo.valueName);
end
end
end)
-- Add Setmetatable Variable Declaration
table.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(self.setMetatableVarScope, {self.setMetatableVarId}, {
Ast.VariableExpression(self.setMetatableVarScope:resolveGlobal("setmetatable"))
}));
end
return ProxifyLocals;
================================================
FILE: src/prometheus/steps/SplitStrings.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- SplitStrings.lua
--
-- This Script provides a Simple Obfuscation Step for splitting Strings
local Step = require("prometheus.step");
local Ast = require("prometheus.ast");
local visitAst = require("prometheus.visitast");
local Parser = require("prometheus.parser");
local util = require("prometheus.util");
local enums = require("prometheus.enums")
local LuaVersion = enums.LuaVersion;
local SplitStrings = Step:extend();
SplitStrings.Description = "This Step splits Strings to a specific or random length";
SplitStrings.Name = "Split Strings";
SplitStrings.SettingsDescriptor = {
Threshold = {
name = "Threshold",
description = "The relative amount of nodes that will be affected",
type = "number",
default = 1,
min = 0,
max = 1,
},
MinLength = {
name = "MinLength",
description = "The minimal length for the chunks in that the Strings are splitted",
type = "number",
default = 5,
min = 1,
max = nil,
},
MaxLength = {
name = "MaxLength",
description = "The maximal length for the chunks in that the Strings are splitted",
type = "number",
default = 5,
min = 1,
max = nil,
},
ConcatenationType = {
name = "ConcatenationType",
description = "The Functions used for Concatenation. Note that when using custom, the String Array will also be Shuffled",
type = "enum",
values = {
"strcat",
"table",
"custom",
},
default = "custom",
},
CustomFunctionType = {
name = "CustomFunctionType",
description = "The Type of Function code injection This Option only applies when custom Concatenation is selected.\
Note that when choosing inline, the code size may increase significantly!",
type = "enum",
values = {
"global",
"local",
"inline",
},
default = "global",
},
CustomLocalFunctionsCount = {
name = "CustomLocalFunctionsCount",
description = "The number of local functions per scope. This option only applies when CustomFunctionType = local",
type = "number",
default = 2,
min = 1,
}
}
function SplitStrings:init(settings) end
local function generateTableConcatNode(chunks, data)
local chunkNodes = {};
for i, chunk in ipairs(chunks) do
table.insert(chunkNodes, Ast.TableEntry(Ast.StringExpression(chunk)));
end
local tb = Ast.TableConstructorExpression(chunkNodes);
data.scope:addReferenceToHigherScope(data.tableConcatScope, data.tableConcatId);
return Ast.FunctionCallExpression(Ast.VariableExpression(data.tableConcatScope, data.tableConcatId), {tb});
end
local function generateStrCatNode(chunks)
-- Put Together Expression for Concatenating String
local generatedNode = nil;
for i, chunk in ipairs(chunks) do
if generatedNode then
generatedNode = Ast.StrCatExpression(generatedNode, Ast.StringExpression(chunk));
else
generatedNode = Ast.StringExpression(chunk);
end
end
return generatedNode
end
local customVariants = 2;
local custom1Code = [=[
function custom(table)
local stringTable, str = table[#table], "";
for i=1,#stringTable, 1 do
str = str .. stringTable[table[i]];
end
return str
end
]=];
local custom2Code = [=[
function custom(tb)
local str = "";
for i=1, #tb / 2, 1 do
str = str .. tb[#tb / 2 + tb[i]];
end
return str
end
]=];
local function generateCustomNodeArgs(chunks, data, variant)
local shuffled = {};
local shuffledIndices = {};
for i = 1, #chunks, 1 do
shuffledIndices[i] = i;
end
util.shuffle(shuffledIndices);
for i, v in ipairs(shuffledIndices) do
shuffled[v] = chunks[i];
end
-- Custom Function Type 1
if variant == 1 then
local args = {};
local tbNodes = {};
for i, v in ipairs(shuffledIndices) do
table.insert(args, Ast.TableEntry(Ast.NumberExpression(v)));
end
for i, chunk in ipairs(shuffled) do
table.insert(tbNodes, Ast.TableEntry(Ast.StringExpression(chunk)));
end
local tb = Ast.TableConstructorExpression(tbNodes);
table.insert(args, Ast.TableEntry(tb));
return {Ast.TableConstructorExpression(args)};
-- Custom Function Type 2
else
local args = {};
for i, v in ipairs(shuffledIndices) do
table.insert(args, Ast.TableEntry(Ast.NumberExpression(v)));
end
for i, chunk in ipairs(shuffled) do
table.insert(args, Ast.TableEntry(Ast.StringExpression(chunk)));
end
return {Ast.TableConstructorExpression(args)};
end
end
local function generateCustomFunctionLiteral(parentScope, variant)
local parser = Parser:new({
LuaVersion = LuaVersion.Lua52;
});
-- Custom Function Type 1
if variant == 1 then
local funcDeclNode = parser:parse(custom1Code).body.statements[1];
local funcBody = funcDeclNode.body;
local funcArgs = funcDeclNode.args;
funcBody.scope:setParent(parentScope);
return Ast.FunctionLiteralExpression(funcArgs, funcBody);
-- Custom Function Type 2
else
local funcDeclNode = parser:parse(custom2Code).body.statements[1];
local funcBody = funcDeclNode.body;
local funcArgs = funcDeclNode.args;
funcBody.scope:setParent(parentScope);
return Ast.FunctionLiteralExpression(funcArgs, funcBody);
end
end
local function generateGlobalCustomFunctionDeclaration(ast, data)
local parser = Parser:new({
LuaVersion = LuaVersion.Lua52;
});
-- Custom Function Type 1
if data.customFunctionVariant == 1 then
local astScope = ast.body.scope;
local funcDeclNode = parser:parse(custom1Code).body.statements[1];
local funcBody = funcDeclNode.body;
local funcArgs = funcDeclNode.args;
funcBody.scope:setParent(astScope);
return Ast.LocalVariableDeclaration(astScope, {data.customFuncId},
{Ast.FunctionLiteralExpression(funcArgs, funcBody)});
-- Custom Function Type 2
else
local astScope = ast.body.scope;
local funcDeclNode = parser:parse(custom2Code).body.statements[1];
local funcBody = funcDeclNode.body;
local funcArgs = funcDeclNode.args;
funcBody.scope:setParent(astScope);
return Ast.LocalVariableDeclaration(data.customFuncScope, {data.customFuncId},
{Ast.FunctionLiteralExpression(funcArgs, funcBody)});
end
end
function SplitStrings:variant()
return math.random(1, customVariants);
end
function SplitStrings:apply(ast, pipeline)
local data = {};
if(self.ConcatenationType == "table") then
local scope = ast.body.scope;
local id = scope:addVariable();
data.tableConcatScope = scope;
data.tableConcatId = id;
elseif(self.ConcatenationType == "custom") then
data.customFunctionType = self.CustomFunctionType;
if data.customFunctionType == "global" then
local scope = ast.body.scope;
local id = scope:addVariable();
data.customFuncScope = scope;
data.customFuncId = id;
data.customFunctionVariant = self:variant();
end
end
local customLocalFunctionsCount = self.CustomLocalFunctionsCount;
local self2 = self;
visitAst(ast, function(node, data)
-- Previsit Function
-- Create Local Function declarations
if(self.ConcatenationType == "custom" and data.customFunctionType == "local" and node.kind == Ast.AstKind.Block and node.isFunctionBlock) then
data.functionData.localFunctions = {};
for i = 1, customLocalFunctionsCount, 1 do
local scope = data.scope;
local id = scope:addVariable();
local variant = self:variant();
table.insert(data.functionData.localFunctions, {
scope = scope,
id = id,
variant = variant,
used = false,
});
end
end
end, function(node, data)
-- PostVisit Function
-- Create actual function literals for local customFunctionType
if(self.ConcatenationType == "custom" and data.customFunctionType == "local" and node.kind == Ast.AstKind.Block and node.isFunctionBlock) then
for i, func in ipairs(data.functionData.localFunctions) do
if func.used then
local literal = generateCustomFunctionLiteral(func.scope, func.variant);
table.insert(node.statements, 1, Ast.LocalVariableDeclaration(func.scope, {func.id}, {literal}));
end
end
end
-- Apply Only to String nodes
if(node.kind == Ast.AstKind.StringExpression) then
local str = node.value;
local chunks = {};
local i = 1;
-- Split String into Parts of length between MinLength and MaxLength
while i <= string.len(str) do
local len = math.random(self.MinLength, self.MaxLength);
table.insert(chunks, string.sub(str, i, i + len - 1));
i = i + len;
end
if(#chunks > 1) then
if math.random() < self.Threshold then
if self.ConcatenationType == "strcat" then
node = generateStrCatNode(chunks);
elseif self.ConcatenationType == "table" then
node = generateTableConcatNode(chunks, data);
elseif self.ConcatenationType == "custom" then
if self.CustomFunctionType == "global" then
local args = generateCustomNodeArgs(chunks, data, data.customFunctionVariant);
-- Add Reference for Variable Renaming
data.scope:addReferenceToHigherScope(data.customFuncScope, data.customFuncId);
node = Ast.FunctionCallExpression(Ast.VariableExpression(data.customFuncScope, data.customFuncId), args);
elseif self.CustomFunctionType == "local" then
local lfuncs = data.functionData.localFunctions;
local idx = math.random(1, #lfuncs);
local func = lfuncs[idx];
local args = generateCustomNodeArgs(chunks, data, func.variant);
func.used = true;
-- Add Reference for Variable Renaming
data.scope:addReferenceToHigherScope(func.scope, func.id);
node = Ast.FunctionCallExpression(Ast.VariableExpression(func.scope, func.id), args);
elseif self.CustomFunctionType == "inline" then
local variant = self:variant();
local args = generateCustomNodeArgs(chunks, data, variant);
local literal = generateCustomFunctionLiteral(data.scope, variant);
node = Ast.FunctionCallExpression(literal, args);
end
end
end
end
return node, true;
end
end, data)
if(self.ConcatenationType == "table") then
local globalScope = data.globalScope;
local tableScope, tableId = globalScope:resolve("table")
ast.body.scope:addReferenceToHigherScope(globalScope, tableId);
table.insert(ast.body.statements, 1, Ast.LocalVariableDeclaration(data.tableConcatScope, {data.tableConcatId},
{Ast.IndexExpression(Ast.VariableExpression(tableScope, tableId), Ast.StringExpression("concat"))}));
elseif(self.ConcatenationType == "custom" and self.CustomFunctionType == "global") then
table.insert(ast.body.statements, 1, generateGlobalCustomFunctionDeclaration(ast, data));
end
end
return SplitStrings;
================================================
FILE: src/prometheus/steps/Vmify.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- Vmify.lua
--
-- This Script provides a Complex Obfuscation Step that will compile the entire Script to a fully custom bytecode that does not share it's instructions
-- with lua, making it much harder to crack than other lua obfuscators
local Step = require("prometheus.step");
local Compiler = require("prometheus.compiler.compiler");
local Vmify = Step:extend();
Vmify.Description = "This Step will Compile your script into a fully-custom (not a half custom like other lua obfuscators) Bytecode Format and emit a vm for executing it.";
Vmify.Name = "Vmify";
Vmify.SettingsDescriptor = {}
function Vmify:init(_) end
function Vmify:apply(ast)
-- Create Compiler
local compiler = Compiler:new();
-- Compile the Script into a bytecode vm
return compiler:compile(ast);
end
return Vmify;
================================================
FILE: src/prometheus/steps/Watermark.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- Watermark.lua
--
-- This Script provides a Step that will add a watermark to the script
local Step = require("prometheus.step");
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local Watermark = Step:extend();
Watermark.Description = "This Step will add a watermark to the script";
Watermark.Name = "Watermark";
Watermark.SettingsDescriptor = {
Content = {
name = "Content",
description = "The Content of the Watermark",
type = "string",
default = "This Script is Part of the Prometheus Obfuscator by Levno_710",
},
CustomVariable = {
name = "Custom Variable",
description = "The Variable that will be used for the Watermark",
type = "string",
default = "_WATERMARK",
}
}
function Watermark:init(settings)
end
function Watermark:apply(ast)
local body = ast.body;
if string.len(self.Content) > 0 then
local scope, variable = ast.globalScope:resolve(self.CustomVariable);
local watermark = Ast.AssignmentVariable(ast.globalScope, variable);
local functionScope = Scope:new(body.scope);
functionScope:addReferenceToHigherScope(ast.globalScope, variable);
local arg = functionScope:addVariable();
local statement = Ast.PassSelfFunctionCallStatement(Ast.StringExpression(self.Content), "gsub", {
Ast.StringExpression(".+"),
Ast.FunctionLiteralExpression({
Ast.VariableExpression(functionScope, arg)
}, Ast.Block({
Ast.AssignmentStatement({
watermark
}, {
Ast.VariableExpression(functionScope, arg)
})
}, functionScope))
});
table.insert(ast.body.statements, 1, statement)
end
end
return Watermark;
================================================
FILE: src/prometheus/steps/WatermarkCheck.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- WatermarkCheck.lua
--
-- This Script provides a Step that will add a watermark to the script
local Step = require("prometheus.step");
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local Watermark = require("prometheus.steps.Watermark");
local WatermarkCheck = Step:extend();
WatermarkCheck.Description = "This Step will add a watermark to the script";
WatermarkCheck.Name = "WatermarkCheck";
WatermarkCheck.SettingsDescriptor = {
Content = {
name = "Content",
description = "The Content of the WatermarkCheck",
type = "string",
default = "This Script is Part of the Prometheus Obfuscator by Levno_710",
},
}
local function callNameGenerator(generatorFunction, ...)
if(type(generatorFunction) == "table") then
generatorFunction = generatorFunction.generateName;
end
return generatorFunction(...);
end
function WatermarkCheck:init(_) end
function WatermarkCheck:apply(ast, pipeline)
self.CustomVariable = "_" .. callNameGenerator(pipeline.namegenerator, math.random(10000000000, 100000000000));
pipeline:addStep(Watermark:new(self));
local body = ast.body;
local watermarkExpression = Ast.StringExpression(self.Content);
local scope, variable = ast.globalScope:resolve(self.CustomVariable);
local watermark = Ast.VariableExpression(ast.globalScope, variable);
local notEqualsExpression = Ast.NotEqualsExpression(watermark, watermarkExpression);
local ifBody = Ast.Block({Ast.ReturnStatement({})}, Scope:new(ast.body.scope));
table.insert(body.statements, 1, Ast.IfStatement(notEqualsExpression, ifBody, {}, nil));
end
return WatermarkCheck;
================================================
FILE: src/prometheus/steps/WrapInFunction.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- WrapInFunction.lua
--
-- This Script provides a Simple Obfuscation Step that wraps the entire Script into a function
local Step = require("prometheus.step");
local Ast = require("prometheus.ast");
local Scope = require("prometheus.scope");
local WrapInFunction = Step:extend();
WrapInFunction.Description = "This Step Wraps the Entire Script into a Function";
WrapInFunction.Name = "Wrap in Function";
WrapInFunction.SettingsDescriptor = {
Iterations = {
name = "Iterations",
description = "The Number Of Iterations",
type = "number",
default = 1,
min = 1,
max = nil,
}
}
function WrapInFunction:init(_) end
function WrapInFunction:apply(ast)
for i = 1, self.Iterations, 1 do
local body = ast.body;
local scope = Scope:new(ast.globalScope);
body.scope:setParent(scope);
ast.body = Ast.Block({
Ast.ReturnStatement({
Ast.FunctionCallExpression(Ast.FunctionLiteralExpression({Ast.VarargExpression()}, body), {Ast.VarargExpression()})
});
}, scope);
end
end
return WrapInFunction;
================================================
FILE: src/prometheus/steps.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- steps.lua
--
-- This Script provides a collection of obfuscation steps.
return {
WrapInFunction = require("prometheus.steps.WrapInFunction"),
SplitStrings = require("prometheus.steps.SplitStrings"),
Vmify = require("prometheus.steps.Vmify"),
ConstantArray = require("prometheus.steps.ConstantArray"),
ProxifyLocals = require("prometheus.steps.ProxifyLocals"),
AntiTamper = require("prometheus.steps.AntiTamper"),
EncryptStrings = require("prometheus.steps.EncryptStrings"),
NumbersToExpressions = require("prometheus.steps.NumbersToExpressions"),
AddVararg = require("prometheus.steps.AddVararg"),
WatermarkCheck = require("prometheus.steps.WatermarkCheck"),
}
================================================
FILE: src/prometheus/tokenizer.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- tokenizer.lua
-- Overview:
-- This Script provides a class for lexical Analysis of lua code.
-- This Tokenizer is Capable of tokenizing LuaU and Lua5.1
local Enums = require("prometheus.enums");
local util = require("prometheus.util");
local logger = require("logger");
local config = require("config");
local LuaVersion = Enums.LuaVersion;
local lookupify = util.lookupify;
local unlookupify = util.unlookupify;
local escape = util.escape;
local chararray = util.chararray;
local keys = util.keys;
local Tokenizer = {};
Tokenizer.EOF_CHAR = "";
Tokenizer.WHITESPACE_CHARS = lookupify{
" ", "\t", "\n", "\r",
}
Tokenizer.ANNOTATION_CHARS = lookupify(chararray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"))
Tokenizer.ANNOTATION_START_CHARS = lookupify(chararray("!@"))
Tokenizer.Conventions = Enums.Conventions;
Tokenizer.TokenKind = {
Eof = "Eof",
Keyword = "Keyword",
Symbol = "Symbol",
Ident = "Identifier",
Number = "Number",
String = "String",
}
Tokenizer.EOF_TOKEN = {
kind = Tokenizer.TokenKind.Eof,
value = "",
startPos = -1,
endPos = -1,
source = "",
}
local function token(self, startPos, kind, value)
local line, linePos = self:getPosition(self.index);
local annotations = self.annotations
self.annotations = {};
return {
kind = kind,
value = value,
startPos = startPos,
endPos = self.index,
source = self.source:sub(startPos + 1, self.index),
line = line,
linePos = linePos,
annotations = annotations,
}
end
local function generateError(self, message)
local line, linePos = self:getPosition(self.index);
return "Lexing Error at Position " .. tostring(line) .. ":" .. tostring(linePos) .. ", " .. message;
end
local function generateWarning(token, message)
return "Warning at Position " .. tostring(token.line) .. ":" .. tostring(token.linePos) .. ", " .. message;
end
function Tokenizer:getPosition(i)
local column = self.columnMap[i]
if not column then --// `i` is bigger than self.length, this shouldn't happen, but it did. (Theres probably some error in the tokenizer, cant find it.)
column = self.columnMap[#self.columnMap]
end
return column.id, column.charMap[i]
end
--// Prepare columnMap for getPosition
function Tokenizer:prepareGetPosition()
local columnMap, column = {}, { charMap = {}, id = 1, length = 0 }
for index = 1, self.length do
local character = string.sub(self.source, index, index) -- NOTE_1: this could use table.clone to reduce amount of NEWTABLE (if that causes any performance issues)
local columnLength = column.length + 1
column.length = columnLength
column.charMap[index] = columnLength
if character == "\n" then
column = { charMap = {}, id = column.id + 1, length = 0 } -- NOTE_1
end
columnMap[index] = column
end
self.columnMap = columnMap
end
-- Constructor for Tokenizer
function Tokenizer:new(settings)
local luaVersion = (settings and (settings.luaVersion or settings.LuaVersion)) or LuaVersion.LuaU;
local conventions = Tokenizer.Conventions[luaVersion];
if(conventions == nil) then
logger:error("The Lua Version \"" .. luaVersion .. "\" is not recognized by the Tokenizer! Please use one of the following: \"" .. table.concat(keys(Tokenizer.Conventions), "\",\"") .. "\"");
end
local tokenizer = {
index = 0, -- Index where the current char is read
length = 0,
source = "", -- Source to Tokenize
luaVersion = luaVersion, -- LuaVersion to be used while Tokenizing
conventions = conventions;
NumberChars = conventions.NumberChars,
NumberCharsLookup = lookupify(conventions.NumberChars),
Keywords = conventions.Keywords,
KeywordsLookup = lookupify(conventions.Keywords),
BinaryNumberChars = conventions.BinaryNumberChars,
BinaryNumberCharsLookup = lookupify(conventions.BinaryNumberChars);
BinaryNums = conventions.BinaryNums,
HexadecimalNums = conventions.HexadecimalNums,
HexNumberChars = conventions.HexNumberChars,
HexNumberCharsLookup = lookupify(conventions.HexNumberChars),
DecimalExponent = conventions.DecimalExponent,
DecimalSeperators = conventions.DecimalSeperators,
IdentChars = conventions.IdentChars,
IdentCharsLookup = lookupify(conventions.IdentChars),
EscapeSequences = conventions.EscapeSequences,
NumericalEscapes = conventions.NumericalEscapes,
EscapeZIgnoreNextWhitespace = conventions.EscapeZIgnoreNextWhitespace,
HexEscapes = conventions.HexEscapes,
UnicodeEscapes = conventions.UnicodeEscapes,
SymbolChars = conventions.SymbolChars,
SymbolCharsLookup = lookupify(conventions.SymbolChars),
MaxSymbolLength = conventions.MaxSymbolLength,
Symbols = conventions.Symbols,
SymbolsLookup = lookupify(conventions.Symbols),
StringStartLookup = lookupify({"\"", "\'"}),
annotations = {},
};
setmetatable(tokenizer, self);
self.__index = self;
return tokenizer;
end
-- Reset State of Tokenizer to Tokenize another File
function Tokenizer:reset()
self.index = 0;
self.length = 0;
self.source = "";
self.annotations = {};
self.columnMap = {};
end
-- Append String to this Tokenizer
function Tokenizer:append(code)
self.source = self.source .. code
self.length = self.length + code:len();
self:prepareGetPosition();
end
-- Function to peek the n'th char in the source of the tokenizer
local function peek(self, n)
n = n or 0;
local i = self.index + n + 1;
if i > self.length then
return Tokenizer.EOF_CHAR
end
return self.source:sub(i, i);
end
-- Function to get the next char in the source
local function get(self)
local i = self.index + 1;
if i > self.length then
logger:error(generateError(self, "Unexpected end of Input"));
end
self.index = self.index + 1;
return self.source:sub(i, i);
end
-- The same as get except it throws an Error if the char is not contained in charOrLookup
local function expect(self, charOrLookup)
if(type(charOrLookup) == "string") then
charOrLookup = {[charOrLookup] = true};
end
local char = peek(self);
if charOrLookup[char] ~= true then
local etb = unlookupify(charOrLookup);
for i, v in ipairs(etb) do
etb[i] = escape(v);
end
local errorMessage = "Unexpected char \"" .. escape(char) .. "\"! Expected one of \"" .. table.concat(etb, "\",\"") .. "\"";
logger:error(generateError(self, errorMessage));
end
self.index = self.index + 1;
return char;
end
-- Returns wether the n'th char is in the lookup
local function is(self, charOrLookup, n)
local char = peek(self, n);
if(type(charOrLookup) == "string") then
return char == charOrLookup;
end
return charOrLookup[char];
end
function Tokenizer:parseAnnotation()
if is(self, Tokenizer.ANNOTATION_START_CHARS) then
self.index = self.index + 1;
local source, length = {}, 0;
while(is(self, Tokenizer.ANNOTATION_CHARS)) do
source[length + 1] = get(self)
length = #source
end
if length > 0 then
self.annotations[string.lower(table.concat(source))] = true;
end
return nil;
end
return get(self);
end
-- skip one or 0 Comments and return wether one was found
function Tokenizer:skipComment()
if(is(self, "-", 0) and is(self, "-", 1)) then
self.index = self.index + 2;
if(is(self, "[")) then
self.index = self.index + 1;
local eqCount = 0;
while(is(self, "=")) do
self.index = self.index + 1;
eqCount = eqCount + 1;
end
if(is(self, "[")) then
-- Multiline Comment
-- Get all Chars to Closing bracket but also consider that the count of equal signs must be the same
while true do
if(self:parseAnnotation() == ']') then
local eqCount2 = 0;
while(is(self, "=")) do
self.index = self.index + 1;
eqCount2 = eqCount2 + 1;
end
if(is(self, "]")) then
if(eqCount2 == eqCount) then
self.index = self.index + 1;
return true
end
end
end
end
end
end
-- Single Line Comment
-- Get all Chars to next Newline
while(self.index < self.length and self:parseAnnotation() ~= "\n") do end
return true;
end
return false;
end
-- skip All Whitespace and Comments to next Token
function Tokenizer:skipWhitespaceAndComments()
while self:skipComment() do end
while is(self, Tokenizer.WHITESPACE_CHARS) do
self.index = self.index + 1;
while self:skipComment() do end
end
end
local function int(self, chars, seperators)
local buffer = {};
while true do
if (is(self, chars)) then
buffer[#buffer + 1] = get(self)
elseif (is(self, seperators)) then
self.index = self.index + 1;
else
break
end
end
return table.concat(buffer);
end
-- Lex the next token as a Number
function Tokenizer:number()
local startPos = self.index;
local source = expect(self, setmetatable({["."] = true}, {__index = self.NumberCharsLookup}));
if source == "0" then
if self.BinaryNums and is(self, lookupify(self.BinaryNums)) then
self.index = self.index + 1;
source = int(self, self.BinaryNumberCharsLookup, lookupify(self.DecimalSeperators or {}));
local value = tonumber(source, 2);
return token(self, startPos, Tokenizer.TokenKind.Number, value);
end
if self.HexadecimalNums and is(self, lookupify(self.HexadecimalNums)) then
self.index = self.index + 1;
source = int(self, self.HexNumberCharsLookup, lookupify(self.DecimalSeperators or {}));
local value = tonumber(source, 16);
return token(self, startPos, Tokenizer.TokenKind.Number, value);
end
end
if source == "." then
source = source .. int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));
else
source = source .. int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));
if(is(self, ".")) then
source = source .. get(self) .. int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));
end
end
if(self.DecimalExponent and is(self, lookupify(self.DecimalExponent))) then
source = source .. get(self);
if(is(self, lookupify({"+","-"}))) then
source = source .. get(self);
end
local v = int(self, self.NumberCharsLookup, lookupify(self.DecimalSeperators or {}));
if(v:len() < 1) then
logger:error(generateError(self, "Expected a Valid Exponent!"));
end
source = source .. v;
end
local value = tonumber(source);
return token(self, startPos, Tokenizer.TokenKind.Number, value);
end
-- Lex the Next Token as Identifier or Keyword
function Tokenizer:ident()
local startPos = self.index;
local source = expect(self, self.IdentCharsLookup)
local sourceAddContent = {source}
while(is(self, self.IdentCharsLookup)) do
-- source = source .. get(self);
table.insert(sourceAddContent, get(self))
end
source = table.concat(sourceAddContent)
if(self.KeywordsLookup[source]) then
return token(self, startPos, Tokenizer.TokenKind.Keyword, source);
end
local tk = token(self, startPos, Tokenizer.TokenKind.Ident, source);
if(string.sub(source, 1, string.len(config.IdentPrefix)) == config.IdentPrefix) then
logger:warn(generateWarning(tk, string.format("identifiers should not start with \"%s\" as this may break the program", config.IdentPrefix)));
end
return tk;
end
function Tokenizer:singleLineString()
local startPos = self.index;
local startChar = expect(self, self.StringStartLookup);
local buffer = {};
while (not is(self, startChar)) do
local char = get(self);
-- Single Line String may not contain Linebreaks except when they are escaped by \
if(char == '\n') then
self.index = self.index - 1;
logger:error(generateError(self, "Unterminated String"));
end
if(char == "\\") then
char = get(self);
local escape = self.EscapeSequences[char];
if(type(escape) == "string") then
char = escape;
elseif(self.NumericalEscapes and self.NumberCharsLookup[char]) then
local numstr = char;
if(is(self, self.NumberCharsLookup)) then
char = get(self);
numstr = numstr .. char;
end
if(is(self, self.NumberCharsLookup)) then
char = get(self);
numstr = numstr .. char;
end
char = string.char(tonumber(numstr));
elseif(self.UnicodeEscapes and char == "u") then
expect(self, "{");
local num = "";
while (is(self, self.HexNumberCharsLookup)) do
num = num .. get(self);
end
expect(self, "}");
char = util.utf8char(tonumber(num, 16));
elseif(self.HexEscapes and char == "x") then
local hex = expect(self, self.HexNumberCharsLookup) .. expect(self, self.HexNumberCharsLookup);
char = string.char(tonumber(hex, 16));
elseif(self.EscapeZIgnoreNextWhitespace and char == "z") then
char = "";
while(is(self, Tokenizer.WHITESPACE_CHARS)) do
self.index = self.index + 1;
end
end
end
--// since table.insert is slower in lua51
buffer[#buffer + 1] = char
end
expect(self, startChar);
return token(self, startPos, Tokenizer.TokenKind.String, table.concat(buffer))
end
function Tokenizer:multiLineString()
local startPos = self.index;
if(is(self, "[")) then
self.index = self.index + 1;
local eqCount = 0;
while(is(self, "=")) do
self.index = self.index + 1;
eqCount = eqCount + 1;
end
if(is(self, "[")) then
-- Multiline String
-- Parse String to Closing bracket but also consider that the count of equal signs must be the same
-- Skip Leading newline if existing
self.index = self.index + 1;
if(is(self, "\n")) then
self.index = self.index + 1;
end
local value = "";
while true do
local char = get(self);
if(char == ']') then
local eqCount2 = 0;
while(is(self, "=")) do
char = char .. get(self);
eqCount2 = eqCount2 + 1;
end
if(is(self, "]")) then
if(eqCount2 == eqCount) then
self.index = self.index + 1;
return token(self, startPos, Tokenizer.TokenKind.String, value), true
end
end
end
value = value .. char;
end
end
end
self.index = startPos;
return nil, false -- There was not an actual multiline string at the given Position
end
function Tokenizer:symbol()
local startPos = self.index;
for len = self.MaxSymbolLength, 1, -1 do
local str = self.source:sub(self.index + 1, self.index + len);
if self.SymbolsLookup[str] then
self.index = self.index + len;
return token(self, startPos, Tokenizer.TokenKind.Symbol, str);
end
end
logger:error(generateError(self, "Unknown Symbol"));
end
-- get the Next token
function Tokenizer:next()
-- Skip All Whitespace before the token
self:skipWhitespaceAndComments();
local startPos = self.index;
if startPos >= self.length then
return token(self, startPos, Tokenizer.TokenKind.Eof);
end
-- Numbers
if(is(self, self.NumberCharsLookup)) then
return self:number();
end
-- Identifiers and Keywords
if(is(self, self.IdentCharsLookup)) then
return self:ident();
end
-- Singleline String Literals
if(is(self, self.StringStartLookup)) then
return self:singleLineString();
end
-- Multiline String Literals
if(is(self, "[", 0)) then
-- The isString variable is due to the fact that "[" could also be a symbol for indexing
local value, isString = self:multiLineString();
if isString then
return value;
end
end
-- Number starting with dot
if(is(self, ".") and is(self, self.NumberCharsLookup, 1)) then
return self:number();
end
-- Symbols
if(is(self, self.SymbolCharsLookup)) then
return self:symbol();
end
logger:error(generateError(self, "Unexpected char \"" .. escape(peek(self)) .. "\"!"));
end
function Tokenizer:scanAll()
local tb = {};
repeat
local token = self:next();
table.insert(tb, token);
until token.kind == Tokenizer.TokenKind.Eof
return tb
end
return Tokenizer
================================================
FILE: src/prometheus/unparser.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- unparser.lua
-- Overview:
-- This Script provides a class for lua code generation from an ast
-- This UnParser is Capable of generating LuaU and Lua5.1
--
-- Note that a LuaU ast can only be unparsed as LuaU if it contains any continue statements
--
-- Settings Object:
-- luaVersion : The LuaVersion of the Script
local config = require("config");
local Ast = require("prometheus.ast");
local Enums = require("prometheus.enums");
local util = require("prometheus.util");
local logger = require("logger");
local lookupify = util.lookupify;
local LuaVersion = Enums.LuaVersion;
local AstKind = Ast.AstKind;
local Unparser = {}
Unparser.SPACE = config.SPACE;
Unparser.TAB = config.TAB;
local function escapeString(str)
str = util.escape(str)
return str;
end
function Unparser:new(settings)
local luaVersion = settings.LuaVersion or LuaVersion.LuaU;
local conventions = Enums.Conventions[luaVersion];
local unparser = {
luaVersion = luaVersion;
conventions = conventions;
identCharsLookup = lookupify(conventions.IdentChars);
numberCharsLookup = lookupify(conventions.NumberChars);
prettyPrint = settings and settings.PrettyPrint or false;
notIdentPattern = "[^" .. table.concat(conventions.IdentChars, "") .. "]";
numberPattern = "^[" .. table.concat(conventions.NumberChars, "") .. "]";
highlight = settings and settings.Highlight or false;
keywordsLookup = lookupify(conventions.Keywords);
}
setmetatable(unparser, self);
self.__index = self;
return unparser;
end
function Unparser:isValidIdentifier(source)
if(string.find(source, self.notIdentPattern)) then
return false;
end
if(string.find(source, self.numberPattern)) then
return false;
end
if self.keywordsLookup[source] then
return false;
end
return #source > 0;
end
function Unparser:setPrettyPrint(prettyPrint)
self.prettyPrint = prettyPrint;
end
function Unparser:getPrettyPrint()
return self.prettyPrint;
end
function Unparser:tabs(i, ws_needed)
return self.prettyPrint and string.rep(self.TAB, i) or ws_needed and self.SPACE or "";
end
function Unparser:newline(ws_needed)
return self.prettyPrint and "\n" or ws_needed and self.SPACE or "";
end
function Unparser:whitespaceIfNeeded(following, ws)
if(self.prettyPrint or self.identCharsLookup[string.sub(following, 1, 1)]) then
return ws or self.SPACE;
end
return "";
end
function Unparser:whitespaceIfNeeded2(leading, ws)
if(self.prettyPrint or self.identCharsLookup[string.sub(leading, #leading, #leading)]) then
return ws or self.SPACE;
end
return "";
end
function Unparser:optionalWhitespace(ws)
return self.prettyPrint and (ws or self.SPACE) or "";
end
function Unparser:whitespace(ws)
return self.SPACE or ws;
end
function Unparser:unparse(ast)
if(ast.kind ~= AstKind.TopNode) then
logger:error("Unparser:unparse expects a TopNode as first argument")
end
return self:unparseBlock(ast.body);
end
-- Helper to join parts table (optimized string building)
local function joinParts(parts)
return table.concat(parts)
end
function Unparser:unparseBlock(block, tabbing)
if(#block.statements < 1) then
return self:whitespace();
end
local parts = {}
for i, statement in ipairs(block.statements) do
if(statement.kind ~= AstKind.NopStatement) then
local statementCode = self:unparseStatement(statement, tabbing);
if(not self.prettyPrint and #parts > 0 and string.sub(statementCode, 1, 1) == "(") then
-- This is so that the following works:
-- print("Test");(function() print("Test2") end)();
statementCode = ";" .. statementCode;
end
local ws = self:whitespaceIfNeeded2(#parts > 0 and parts[#parts] or "", self:whitespaceIfNeeded(statementCode, self:newline(true)));
if i ~= 1 then
parts[#parts + 1] = ws;
end
if(self.prettyPrint) then
statementCode = statementCode .. ";"
end
parts[#parts + 1] = statementCode;
end
end
return joinParts(parts);
end
function Unparser:unparseStatement(statement, tabbing)
tabbing = tabbing and tabbing + 1 or 0;
local parts = {};
local function push(...) -- Helper to add multiple strings efficiently
for i = 1, select('#', ...) do
parts[#parts + 1] = select(i, ...)
end
end
if(statement.kind == AstKind.ContinueStatement) then
push("continue");
-- Break Statement
elseif(statement.kind == AstKind.BreakStatement) then
push("break");
-- Do Statement
elseif(statement.kind == AstKind.DoStatement) then
local bodyCode = self:unparseBlock(statement.body, tabbing);
push("do", self:whitespaceIfNeeded(bodyCode, self:newline(true)),
bodyCode, self:newline(false),
self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
-- While Statement
elseif(statement.kind == AstKind.WhileStatement) then
local expressionCode = self:unparseExpression(statement.condition, tabbing);
local bodyCode = self:unparseBlock(statement.body, tabbing);
push("while", self:whitespaceIfNeeded(expressionCode), expressionCode, self:whitespaceIfNeeded2(expressionCode),
"do", self:whitespaceIfNeeded(bodyCode, self:newline(true)),
bodyCode, self:newline(false),
self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
-- Repeat Until Statement
elseif(statement.kind == AstKind.RepeatStatement) then
local expressionCode = self:unparseExpression(statement.condition, tabbing);
local bodyCode = self:unparseBlock(statement.body, tabbing);
push("repeat", self:whitespaceIfNeeded(bodyCode, self:newline(true)),
bodyCode,
self:whitespaceIfNeeded2(bodyCode, self:newline() .. self:tabs(tabbing, true)), "until",
self:whitespaceIfNeeded(expressionCode), expressionCode);
-- For Statement
elseif(statement.kind == AstKind.ForStatement) then
local bodyCode = self:unparseBlock(statement.body, tabbing);
push("for", self:whitespace(), statement.scope:getVariableName(statement.id), self:optionalWhitespace(), "=");
push(self:optionalWhitespace(), self:unparseExpression(statement.initialValue, tabbing), ",");
push(self:optionalWhitespace(), self:unparseExpression(statement.finalValue, tabbing), ",");
local incrementByCode = statement.incrementBy and self:unparseExpression(statement.incrementBy, tabbing) or "1";
push(self:optionalWhitespace(), incrementByCode, self:whitespaceIfNeeded2(incrementByCode), "do",
self:whitespaceIfNeeded(bodyCode, self:newline(true)),
bodyCode, self:newline(false),
self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
-- For In Statement
elseif(statement.kind == AstKind.ForInStatement) then
push("for", self:whitespace());
for i, id in ipairs(statement.ids) do
if(i ~= 1) then
push(",", self:optionalWhitespace());
end
push(statement.scope:getVariableName(id));
end
push(self:whitespace(), "in");
local exprcode = self:unparseExpression(statement.expressions[1], tabbing);
push(self:whitespaceIfNeeded(exprcode), exprcode);
for i = 2, #statement.expressions, 1 do
exprcode = self:unparseExpression(statement.expressions[i], tabbing);
push(",", self:optionalWhitespace(), exprcode);
end
local bodyCode = self:unparseBlock(statement.body, tabbing);
push(self:whitespaceIfNeeded2(#parts > 0 and parts[#parts] or ""), "do", self:whitespaceIfNeeded(bodyCode, self:newline(true)),
bodyCode, self:newline(false),
self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
-- If Statement
elseif(statement.kind == AstKind.IfStatement) then
local exprcode = self:unparseExpression(statement.condition, tabbing);
local bodyCode = self:unparseBlock(statement.body, tabbing);
push("if", self:whitespaceIfNeeded(exprcode), exprcode, self:whitespaceIfNeeded2(exprcode), "then",
self:whitespaceIfNeeded(bodyCode, self:newline(true)), bodyCode);
for i, eif in ipairs(statement.elseifs) do
exprcode = self:unparseExpression(eif.condition, tabbing);
bodyCode = self:unparseBlock(eif.body, tabbing);
local lastPart = #parts > 0 and parts[#parts] or "";
push(self:newline(false), self:whitespaceIfNeeded2(lastPart, self:tabs(tabbing, true)),
"elseif", self:whitespaceIfNeeded(exprcode), exprcode, self:whitespaceIfNeeded2(exprcode),
"then", self:whitespaceIfNeeded(bodyCode, self:newline(true)), bodyCode);
end
if(statement.elsebody) then
bodyCode = self:unparseBlock(statement.elsebody, tabbing);
local lastPart = #parts > 0 and parts[#parts] or "";
push(self:newline(false), self:whitespaceIfNeeded2(lastPart, self:tabs(tabbing, true)),
"else", self:whitespaceIfNeeded(bodyCode, self:newline(true)), bodyCode);
end
push(self:newline(false), self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
-- Function Declaration
elseif(statement.kind == AstKind.FunctionDeclaration) then
local funcname = statement.scope:getVariableName(statement.id);
for _, index in ipairs(statement.indices) do
funcname = funcname .. "." .. index;
end
push("function", self:whitespace(), funcname, "(");
for i, arg in ipairs(statement.args) do
if i > 1 then
push(",", self:optionalWhitespace());
end
if(arg.kind == AstKind.VarargExpression) then
push("...");
else
push(arg.scope:getVariableName(arg.id));
end
end
push(")");
local bodyCode = self:unparseBlock(statement.body, tabbing);
push(self:newline(false), bodyCode, self:newline(false),
self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
-- Local Function Declaration
elseif(statement.kind == AstKind.LocalFunctionDeclaration) then
local funcname = statement.scope:getVariableName(statement.id);
push("local", self:whitespace(), "function", self:whitespace(), funcname, "(");
for i, arg in ipairs(statement.args) do
if i > 1 then
push(",", self:optionalWhitespace());
end
if(arg.kind == AstKind.VarargExpression) then
push("...");
else
push(arg.scope:getVariableName(arg.id));
end
end
push(")");
local bodyCode = self:unparseBlock(statement.body, tabbing);
push(self:newline(false), bodyCode, self:newline(false),
self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
-- Local Variable Declaration
elseif(statement.kind == AstKind.LocalVariableDeclaration) then
push("local", self:whitespace());
for i, id in ipairs(statement.ids) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(statement.scope:getVariableName(id));
end
if(#statement.expressions > 0) then
push(self:optionalWhitespace(), "=", self:optionalWhitespace());
for i, expr in ipairs(statement.expressions) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(self:unparseExpression(expr, tabbing + 1));
end
end
-- Function Call Statement
elseif(statement.kind == AstKind.FunctionCallStatement) then
if not (statement.base.kind == AstKind.IndexExpression or statement.base.kind == AstKind.VariableExpression) then
push("(", self:unparseExpression(statement.base, tabbing), ")");
else
push(self:unparseExpression(statement.base, tabbing));
end
push("(");
for i, arg in ipairs(statement.args) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(self:unparseExpression(arg, tabbing));
end
push(")");
-- Pass Self Function Call Statement
elseif(statement.kind == AstKind.PassSelfFunctionCallStatement) then
if not (statement.base.kind == AstKind.IndexExpression or statement.base.kind == AstKind.VariableExpression) then
push("(", self:unparseExpression(statement.base, tabbing), ")");
else
push(self:unparseExpression(statement.base, tabbing));
end
push(":", statement.passSelfFunctionName, "(");
for i, arg in ipairs(statement.args) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(self:unparseExpression(arg, tabbing));
end
push(")");
elseif(statement.kind == AstKind.AssignmentStatement) then
for i, primary_expr in ipairs(statement.lhs) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(self:unparseExpression(primary_expr, tabbing));
end
push(self:optionalWhitespace(), "=", self:optionalWhitespace());
for i, expr in ipairs(statement.rhs) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(self:unparseExpression(expr, tabbing + 1));
end
-- Return Statement
elseif(statement.kind == AstKind.ReturnStatement) then
push("return");
if(#statement.args > 0) then
local exprcode = self:unparseExpression(statement.args[1], tabbing);
push(self:whitespaceIfNeeded(exprcode), exprcode);
for i = 2, #statement.args, 1 do
exprcode = self:unparseExpression(statement.args[i], tabbing);
push(",", self:optionalWhitespace(), exprcode);
end
end
elseif self.luaVersion == LuaVersion.LuaU then
local compoundOperators = {
[AstKind.CompoundAddStatement] = "+=",
[AstKind.CompoundSubStatement] = "-=",
[AstKind.CompoundMulStatement] = "*=",
[AstKind.CompoundDivStatement] = "/=",
[AstKind.CompoundModStatement] = "%=",
[AstKind.CompoundPowStatement] = "^=",
[AstKind.CompoundConcatStatement] = "..=",
}
local operator = compoundOperators[statement.kind]
if operator then
push(self:unparseExpression(statement.lhs, tabbing), self:optionalWhitespace(), operator,
self:optionalWhitespace(), self:unparseExpression(statement.rhs, tabbing));
else
logger:error(string.format("\"%s\" is not a valid unparseable statement in %s!", statement.kind, self.luaVersion))
end
end
return self:tabs(tabbing, false) .. joinParts(parts);
end
function Unparser:unparseExpression(expression, tabbing)
if expression.isParenthesizedExpression then
local unwrapped = {}
for k, v in pairs(expression) do
unwrapped[k] = v
end
unwrapped.isParenthesizedExpression = nil
return "(" .. self:unparseExpression(unwrapped, tabbing) .. ")"
end
local parts = {};
local function push(...)
for i = 1, select('#', ...) do
parts[#parts + 1] = select(i, ...)
end
end
if(expression.kind == AstKind.BooleanExpression) then
return expression.value and "true" or "false";
end
if(expression.kind == AstKind.NumberExpression) then
local str = tostring(expression.value);
if(str == "inf") then
return "2e1024"
end
if(str == "-inf") then
return "-2e1024"
end
if(str:sub(1, 2) == "0.") then
str = str:sub(2);
end
return str;
end
if(expression.kind == AstKind.VariableExpression or expression.kind == AstKind.AssignmentVariable) then
return expression.scope:getVariableName(expression.id);
end
if(expression.kind == AstKind.StringExpression) then
return "\"" .. escapeString(expression.value) .. "\"";
end
if(expression.kind == AstKind.NilExpression) then
return "nil";
end
if(expression.kind == AstKind.VarargExpression) then
return "...";
end
local k = AstKind.OrExpression;
if(expression.kind == k) then
local lhs = self:unparseExpression(expression.lhs, tabbing);
local rhs = self:unparseExpression(expression.rhs, tabbing);
return lhs .. self:whitespaceIfNeeded2(lhs) .. "or" .. self:whitespaceIfNeeded(rhs) .. rhs;
end
k = AstKind.AndExpression;
if(expression.kind == k) then
local lhs = self:unparseExpression(expression.lhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k)) then
lhs = "(" .. lhs .. ")";
end
local rhs = self:unparseExpression(expression.rhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then
rhs = "(" .. rhs .. ")";
end
return lhs .. self:whitespaceIfNeeded2(lhs) .. "and" .. self:whitespaceIfNeeded(rhs) .. rhs;
end
local comparisonOps = {
[AstKind.LessThanExpression] = "<",
[AstKind.GreaterThanExpression] = ">",
[AstKind.LessThanOrEqualsExpression] = "<=",
[AstKind.GreaterThanOrEqualsExpression] = ">=",
[AstKind.NotEqualsExpression] = "~=",
[AstKind.EqualsExpression] = "==",
}
local op = comparisonOps[expression.kind]
if op then
k = expression.kind
local lhs = self:unparseExpression(expression.lhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k)) then
lhs = "(" .. lhs .. ")";
end
local rhs = self:unparseExpression(expression.rhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then
rhs = "(" .. rhs .. ")";
end
return lhs .. self:optionalWhitespace() .. op .. self:optionalWhitespace() .. rhs;
end
k = AstKind.StrCatExpression
if expression.kind == k then
local lhs = self:unparseExpression(expression.lhs, tabbing)
if Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k) then
lhs = "(" .. lhs .. ")"
end
local rhs = self:unparseExpression(expression.rhs, tabbing)
if Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k) then
rhs = "(" .. rhs .. ")"
end
if self.numberCharsLookup[string.sub(lhs, #lhs, #lhs)] then
lhs = lhs .. " "
end
return lhs .. self:optionalWhitespace() .. (tostring(rhs):sub(1, 1) == "." and ".. " or "..") .. self:optionalWhitespace() .. rhs
end
local arithmeticOps = {
[AstKind.AddExpression] = "+",
[AstKind.SubExpression] = "-",
[AstKind.MulExpression] = "*",
[AstKind.DivExpression] = "/",
[AstKind.ModExpression] = "%",
[AstKind.PowExpression] = "^",
}
op = arithmeticOps[expression.kind]
if op then
k = expression.kind
local lhs = self:unparseExpression(expression.lhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.lhs.kind) >= Ast.astKindExpressionToNumber(k)) then
lhs = "(" .. lhs .. ")";
end
local rhs = self:unparseExpression(expression.rhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then
rhs = "(" .. rhs .. ")";
end
if op == "-" and string.sub(rhs, 1, 1) == "-" then
rhs = "(" .. rhs .. ")";
end
return lhs .. self:optionalWhitespace() .. op .. self:optionalWhitespace() .. rhs;
end
-- Unary Expressions
k = AstKind.NotExpression;
if(expression.kind == k) then
local rhs = self:unparseExpression(expression.rhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then
rhs = "(" .. rhs .. ")";
end
return "not" .. self:whitespaceIfNeeded(rhs) .. rhs;
end
k = AstKind.NegateExpression;
if(expression.kind == k) then
local rhs = self:unparseExpression(expression.rhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then
rhs = "(" .. rhs .. ")";
end
if string.sub(rhs, 1, 1) == "-" then
rhs = "(" .. rhs .. ")";
end
return "-" .. rhs;
end
k = AstKind.LenExpression;
if(expression.kind == k) then
local rhs = self:unparseExpression(expression.rhs, tabbing);
if(Ast.astKindExpressionToNumber(expression.rhs.kind) >= Ast.astKindExpressionToNumber(k)) then
rhs = "(" .. rhs .. ")";
end
return "#" .. rhs;
end
k = AstKind.IndexExpression;
if(expression.kind == k or expression.kind == AstKind.AssignmentIndexing) then
local base = self:unparseExpression(expression.base, tabbing);
if(expression.base.kind == AstKind.VarargExpression or Ast.astKindExpressionToNumber(expression.base.kind) > Ast.astKindExpressionToNumber(k) or expression.base.kind == AstKind.StringExpression or expression.base.kind == AstKind.NumberExpression or expression.base.kind == AstKind.NilExpression) then
base = "(" .. base .. ")";
end
-- Identifier Indexing e.g: x.y instead of x["y"];
if(expression.index.kind == AstKind.StringExpression and self:isValidIdentifier(expression.index.value)) then
return base .. "." .. expression.index.value;
end
-- Index never needs parens
local index = self:unparseExpression(expression.index, tabbing);
return base .. "[" .. index .. "]";
end
k = AstKind.FunctionCallExpression;
if(expression.kind == k) then
if not (expression.base.kind == AstKind.IndexExpression or expression.base.kind == AstKind.VariableExpression) then
push("(", self:unparseExpression(expression.base, tabbing), ")");
else
push(self:unparseExpression(expression.base, tabbing));
end
push("(");
for i, arg in ipairs(expression.args) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(self:unparseExpression(arg, tabbing));
end
push(")");
return joinParts(parts);
end
k = AstKind.PassSelfFunctionCallExpression;
if(expression.kind == k) then
if not (expression.base.kind == AstKind.IndexExpression or expression.base.kind == AstKind.VariableExpression) then
push("(", self:unparseExpression(expression.base, tabbing), ")");
else
push(self:unparseExpression(expression.base, tabbing));
end
push(":", expression.passSelfFunctionName, "(");
for i, arg in ipairs(expression.args) do
if i > 1 then
push(",", self:optionalWhitespace());
end
push(self:unparseExpression(arg, tabbing));
end
push(")");
return joinParts(parts);
end
k = AstKind.FunctionLiteralExpression;
if(expression.kind == k) then
push("function", "(");
for i, arg in ipairs(expression.args) do
if i > 1 then
push(",", self:optionalWhitespace());
end
if(arg.kind == AstKind.VarargExpression) then
push("...");
else
push(arg.scope:getVariableName(arg.id));
end
end
push(")");
local bodyCode = self:unparseBlock(expression.body, tabbing);
push(self:newline(false), bodyCode, self:newline(false),
self:whitespaceIfNeeded2(bodyCode, self:tabs(tabbing, true)), "end");
return joinParts(parts);
end
k = AstKind.TableConstructorExpression;
if(expression.kind == k) then
if(#expression.entries == 0) then return "{}" end;
local inlineTable = #expression.entries <= 3;
local tableTabbing = tabbing + 1;
push("{");
if inlineTable then
push(self:optionalWhitespace());
else
push(self:optionalWhitespace(self:newline() .. self:tabs(tableTabbing)));
end
local p = false;
for i, entry in ipairs(expression.entries) do
p = true;
local sep = self.prettyPrint and "," or (math.random(1, 2) == 1 and "," or ";");
if i > 1 and not inlineTable then
push(sep, self:optionalWhitespace(self:newline() .. self:tabs(tableTabbing)));
elseif i > 1 then
push(sep, self:optionalWhitespace());
end
if(entry.kind == AstKind.KeyedTableEntry) then
if(entry.key.kind == AstKind.StringExpression and self:isValidIdentifier(entry.key.value)) then
push(entry.key.value);
else
push("[", self:unparseExpression(entry.key, tableTabbing), "]");
end
push(self:optionalWhitespace(), "=", self:optionalWhitespace(), self:unparseExpression(entry.value, tableTabbing));
else
push(self:unparseExpression(entry.value, tableTabbing));
end
end
if inlineTable then
return joinParts(parts) .. self:optionalWhitespace() .. "}";
end
return joinParts(parts) .. self:optionalWhitespace((p and "," or "") .. self:newline() .. self:tabs(tabbing)) .. "}";
end
if (self.luaVersion == LuaVersion.LuaU) then
k = AstKind.IfElseExpression
if(expression.kind == k) then
push("if ");
push(self:unparseExpression(expression.condition));
push(" then ");
push(self:unparseExpression(expression.true_value));
push(" else ");
push(self:unparseExpression(expression.false_value));
return joinParts(parts);
end
end
logger:error(string.format("\"%s\" is not a valid unparseable expression", expression.kind));
end
return Unparser
================================================
FILE: src/prometheus/util.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- util.lua
--
-- This Script provides some utility functions for Prometheus.
local function lookupify(tb)
local tb2 = {};
for _, v in ipairs(tb) do
tb2[v] = true
end
return tb2
end
local function unlookupify(tb)
local tb2 = {};
for v, _ in pairs(tb) do
table.insert(tb2, v);
end
return tb2;
end
local function escape(str)
return str:gsub(".", function(char)
local byte = string.byte(char)
if byte >= 32 and byte <= 126 and char ~= "\\" and char ~= "\"" and char ~= "\'" then
return char
end
if(char == "\\") then
return "\\\\";
end
if(char == "\n") then
return "\\n";
end
if(char == "\r") then
return "\\r";
end
if(char == "\"") then
return "\\\"";
end
if(char == "\'") then
return "\\\'";
end
return string.format("\\%03d", byte);
end)
end
local function chararray(str)
local tb = {};
for i = 1, str:len(), 1 do
table.insert(tb, str:sub(i, i));
end
return tb;
end
local function keys(tb)
local keyset = {}
local n=0
for k, _ in pairs(tb) do
n = n + 1
keyset[n] = k
end
return keyset
end
local utf8char;
do
local string_char = string.char
function utf8char(cp)
if cp < 128 then
return string_char(cp)
end
local suffix = cp % 64
local c4 = 128 + suffix
cp = (cp - suffix) / 64
if cp < 32 then
return string_char(192 + cp, c4)
end
suffix = cp % 64
local c3 = 128 + suffix
cp = (cp - suffix) / 64
if cp < 16 then
return string_char(224 + cp, c3, c4)
end
suffix = cp % 64
cp = (cp - suffix) / 64
return string_char(240 + cp, 128 + suffix, c3, c4)
end
end
local function shuffle(tb)
for i = #tb, 2, -1 do
local j = math.random(i)
tb[i], tb[j] = tb[j], tb[i]
end
return tb
end
local function readonly(obj)
local r = newproxy(true);
getmetatable(r).__index = obj;
return r;
end
return {
lookupify = lookupify,
unlookupify = unlookupify,
escape = escape,
chararray = chararray,
keys = keys,
shuffle = shuffle,
utf8char = utf8char,
readonly = readonly
}
================================================
FILE: src/prometheus/visitast.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- visitast.lua
--
-- This Script provides a utility function for visiting each node of an AST.
local Ast = require("prometheus.ast");
local util = require("prometheus.util");
local AstKind = Ast.AstKind;
local lookupify = util.lookupify;
local visitAst, visitBlock, visitStatement, visitExpression;
function visitAst(ast, previsit, postvisit, data)
ast.isAst = true;
data = data or {};
data.scopeStack = {};
data.functionData = {
depth = 0;
scope = ast.body.scope;
node = ast;
};
data.scope = ast.globalScope;
data.globalScope = ast.globalScope;
if(type(previsit) == "function") then
local node, skip = previsit(ast, data);
ast = node or ast;
if skip then
return ast;
end
end
-- Is Function Block because global scope is treated like a Function
visitBlock(ast.body, previsit, postvisit, data, true);
if(type(postvisit) == "function") then
ast = postvisit(ast, data) or ast;
end
return ast;
end
local compundStats = lookupify{
AstKind.CompoundAddStatement,
AstKind.CompoundSubStatement,
AstKind.CompoundMulStatement,
AstKind.CompoundDivStatement,
AstKind.CompoundModStatement,
AstKind.CompoundPowStatement,
AstKind.CompoundConcatStatement,
}
function visitBlock(block, previsit, postvisit, data, isFunctionBlock)
block.isBlock = true;
block.isFunctionBlock = isFunctionBlock or false;
data.scope = block.scope;
local parentBlockData = data.blockData;
data.blockData = {};
table.insert(data.scopeStack, block.scope);
if(type(previsit) == "function") then
local node, skip = previsit(block, data);
block = node or block;
if skip then
data.scope = table.remove(data.scopeStack);
return block
end
end
local i = 1;
while i <= #block.statements do
local statement = table.remove(block.statements, i);
i = i - 1;
local returnedStatements = {visitStatement(statement, previsit, postvisit, data)};
for j, statement in ipairs(returnedStatements) do
i = i + 1;
table.insert(block.statements, i, statement);
end
i = i + 1;
end
if(type(postvisit) == "function") then
block = postvisit(block, data) or block;
end
data.scope = table.remove(data.scopeStack);
data.blockData = parentBlockData;
return block;
end
function visitStatement(statement, previsit, postvisit, data)
statement.isStatement = true;
if(type(previsit) == "function") then
local node, skip = previsit(statement, data);
statement = node or statement;
if skip then
return statement;
end
end
-- Visit Child Nodes of Statement
if(statement.kind == AstKind.ReturnStatement) then
for i, expression in ipairs(statement.args) do
statement.args[i] = visitExpression(expression, previsit, postvisit, data);
end
elseif(statement.kind == AstKind.PassSelfFunctionCallStatement or statement.kind == AstKind.FunctionCallStatement) then
statement.base = visitExpression(statement.base, previsit, postvisit, data);
for i, expression in ipairs(statement.args) do
statement.args[i] = visitExpression(expression, previsit, postvisit, data);
end
elseif(statement.kind == AstKind.AssignmentStatement) then
for i, primaryExpr in ipairs(statement.lhs) do
statement.lhs[i] = visitExpression(primaryExpr, previsit, postvisit, data);
end
for i, expression in ipairs(statement.rhs) do
statement.rhs[i] = visitExpression(expression, previsit, postvisit, data);
end
elseif(statement.kind == AstKind.FunctionDeclaration or statement.kind == AstKind.LocalFunctionDeclaration) then
local parentFunctionData = data.functionData;
data.functionData = {
depth = parentFunctionData.depth + 1;
scope = statement.body.scope;
node = statement;
};
statement.body = visitBlock(statement.body, previsit, postvisit, data, true);
data.functionData = parentFunctionData;
elseif(statement.kind == AstKind.DoStatement) then
statement.body = visitBlock(statement.body, previsit, postvisit, data, false);
elseif(statement.kind == AstKind.WhileStatement) then
statement.condition = visitExpression(statement.condition, previsit, postvisit, data);
statement.body = visitBlock(statement.body, previsit, postvisit, data, false);
elseif(statement.kind == AstKind.RepeatStatement) then
statement.body = visitBlock(statement.body, previsit, postvisit, data);
statement.condition = visitExpression(statement.condition, previsit, postvisit, data);
elseif(statement.kind == AstKind.ForStatement) then
statement.initialValue = visitExpression(statement.initialValue, previsit, postvisit, data);
statement.finalValue = visitExpression(statement.finalValue, previsit, postvisit, data);
statement.incrementBy = visitExpression(statement.incrementBy, previsit, postvisit, data);
statement.body = visitBlock(statement.body, previsit, postvisit, data, false);
elseif(statement.kind == AstKind.ForInStatement) then
for i, expression in ipairs(statement.expressions) do
statement.expressions[i] = visitExpression(expression, previsit, postvisit, data);
end
visitBlock(statement.body, previsit, postvisit, data, false);
elseif(statement.kind == AstKind.IfStatement) then
statement.condition = visitExpression(statement.condition, previsit, postvisit, data);
statement.body = visitBlock(statement.body, previsit, postvisit, data, false);
for i, eif in ipairs(statement.elseifs) do
eif.condition = visitExpression(eif.condition, previsit, postvisit, data);
eif.body = visitBlock(eif.body, previsit, postvisit, data, false);
end
if(statement.elsebody) then
statement.elsebody = visitBlock(statement.elsebody, previsit, postvisit, data, false);
end
elseif(statement.kind == AstKind.LocalVariableDeclaration) then
for i, expression in ipairs(statement.expressions) do
statement.expressions[i] = visitExpression(expression, previsit, postvisit, data);
end
elseif compundStats[statement.kind] then
statement.lhs = visitExpression(statement.lhs, previsit, postvisit, data);
statement.rhs = visitExpression(statement.rhs, previsit, postvisit, data);
end
if(type(postvisit) == "function") then
local statements = {postvisit(statement, data)};
if #statements > 0 then
return unpack(statements);
end
end
return statement;
end
local binaryExpressions = lookupify{
AstKind.OrExpression,
AstKind.AndExpression,
AstKind.LessThanExpression,
AstKind.GreaterThanExpression,
AstKind.LessThanOrEqualsExpression,
AstKind.GreaterThanOrEqualsExpression,
AstKind.NotEqualsExpression,
AstKind.EqualsExpression,
AstKind.StrCatExpression,
AstKind.AddExpression,
AstKind.SubExpression,
AstKind.MulExpression,
AstKind.DivExpression,
AstKind.ModExpression,
AstKind.PowExpression,
}
function visitExpression(expression, previsit, postvisit, data)
expression.isExpression = true;
if(type(previsit) == "function") then
local node, skip = previsit(expression, data);
expression = node or expression;
if skip then
return expression;
end
end
if(binaryExpressions[expression.kind]) then
expression.lhs = visitExpression(expression.lhs, previsit, postvisit, data);
expression.rhs = visitExpression(expression.rhs, previsit, postvisit, data);
end
if(expression.kind == AstKind.NotExpression or expression.kind == AstKind.NegateExpression or expression.kind == AstKind.LenExpression) then
expression.rhs = visitExpression(expression.rhs, previsit, postvisit, data);
end
if(expression.kind == AstKind.PassSelfFunctionCallExpression or expression.kind == AstKind.FunctionCallExpression) then
expression.base = visitExpression(expression.base, previsit, postvisit, data);
for i, arg in ipairs(expression.args) do
expression.args[i] = visitExpression(arg, previsit, postvisit, data);
end
end
if(expression.kind == AstKind.FunctionLiteralExpression) then
local parentFunctionData = data.functionData;
data.functionData = {
depth = parentFunctionData.depth + 1;
scope = expression.body.scope;
node = expression;
};
expression.body = visitBlock(expression.body, previsit, postvisit, data, true);
data.functionData = parentFunctionData;
end
if(expression.kind == AstKind.TableConstructorExpression) then
for i, entry in ipairs(expression.entries) do
if entry.kind == AstKind.KeyedTableEntry then
entry.key = visitExpression(entry.key, previsit, postvisit, data);
end
entry.value = visitExpression(entry.value, previsit, postvisit, data);
end
end
if(expression.kind == AstKind.IndexExpression or expression.kind == AstKind.AssignmentIndexing) then
expression.base = visitExpression(expression.base, previsit, postvisit, data);
expression.index = visitExpression(expression.index, previsit, postvisit, data);
end
if(expression.kind == AstKind.IfElseExpression) then
expression.condition = visitExpression(expression.condition, previsit, postvisit, data);
expression.true_expr = visitExpression(expression.true_expr, previsit, postvisit, data);
expression.false_expr = visitExpression(expression.false_expr, previsit, postvisit, data);
end
if(type(postvisit) == "function") then
expression = postvisit(expression, data) or expression;
end
return expression;
end
return visitAst;
================================================
FILE: src/prometheus.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- prometheus.lua
--
-- This file is the entrypoint for Prometheus
-- Configure package.path for require
local function script_path()
local str = debug.getinfo(2, "S").source:sub(2)
return str:match("(.*[/%\\])")
end
local oldPkgPath = package.path;
package.path = script_path() .. "?.lua;" .. package.path;
-- Math.random Fix for Lua5.1
-- Check if fix is needed
if not pcall(function()
return math.random(1, 2^40);
end) then
local oldMathRandom = math.random;
math.random = function(a, b)
if not a and b then
return oldMathRandom();
end
if not b then
return math.random(1, a);
end
if a > b then
a, b = b, a;
end
local diff = b - a;
assert(diff >= 0);
if diff > 2 ^ 31 - 1 then
return math.floor(oldMathRandom() * diff + a);
else
return oldMathRandom(a, b);
end
end
end
-- newproxy polyfill
_G.newproxy = _G.newproxy or function(arg)
if arg then
return setmetatable({}, {});
end
return {};
end
-- Require Prometheus Submodules
local Pipeline = require("prometheus.pipeline");
local highlight = require("highlightlua");
local colors = require("colors");
local Logger = require("logger");
local Presets = require("presets");
local Config = require("config");
local util = require("prometheus.util");
-- Restore package.path
package.path = oldPkgPath;
-- Export
return {
Pipeline = Pipeline;
colors = colors;
Config = util.readonly(Config); -- Readonly
Logger = Logger;
highlight = highlight;
Presets = Presets;
}
================================================
FILE: tests/ambiguous-call.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- ambiguous-call.lua
--
-- This Test demonstrates a reproduction for issue #203 where the parser misreads the following as arguments to the previous literal assignment.
local counter = 1
(function()
counter = counter + 1
print("counter:", counter)
end)();
================================================
FILE: tests/closures.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- closures.lua
--
-- This Test demonstrates deterministic closure behavior.
local arr = {}
for i = 1, 100 do
local x;
x = (x or 1) + i;
arr[i] = function()
return x;
end
end
for _, func in ipairs(arr) do
print(func())
end
================================================
FILE: tests/coroutines.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- coroutines.lua
--
-- This Test demonstrates a deterministic coroutine driven sequence generator.
local function squares(limit)
return coroutine.create(function()
for i = 1, limit do
coroutine.yield(i * i)
end
end)
end
local co = squares(6)
while true do
local ok, value = coroutine.resume(co)
if not ok then
error(value)
end
if value == nil then
break
end
print(value)
end
================================================
FILE: tests/fibonacci.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- fibonacci.lua
--
-- This Test demonstrates a simple fibonacci sequence.
local function fibonacci(max)
local a, b = 0, 1
while a < max do
print(a)
a, b = b, a + b
end
end
fibonacci(1000)
================================================
FILE: tests/iterator.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- iterator.lua
--
-- This Test demonstrates a custom iterator that creates a predictable countdown.
local function countdown(startValue, step)
local value = startValue + step
return function()
value = value - step
if value <= 0 then
return nil
end
return value
end
end
for num in countdown(12, 3) do
print(num)
end
================================================
FILE: tests/loops.lua
================================================
--============================================================
-- Iteration Test Suite
-- Target: General purpose
-- Author: SpinnySpiwal
-- Purpose: Validate functionality of iterative loops.
-- Note: this test was rewritten after discovering yet another bug in the compiler.
-- The bug was with negative for statements not functioning. And it slipped past the tests.
-- This test ensures this won't happen again.
--============================================================
local TESTS_PASSED = 0
local byte, floor = string.byte, math.floor
local x, y, z, w, t, expected = nil, nil, nil, nil, nil, nil
local function round(value, precision)
return floor(value * 10^precision) / 10^precision
end
local function str2num(str)
local result = 0
for i=1, #str, 1 do
result = result + byte(str, i)
end
return result
end
--============================================================
-- Test 1: Ascending for loop (integer)
--============================================================
y = 0
for _=1, 100 do
y = y + 1
end
if y ~= 100 then
print("TEST 1: Ascending for loop (integer) FAILED! Expected 100, got " .. y)
else
TESTS_PASSED = TESTS_PASSED + 1
end
--============================================================
-- Test 2: Descending for loop (integer)
--============================================================
z = 0
for _=100, 1, -1 do
z = z + 1
end
if z ~= 100 then
print("TEST 2: Descending for loop (integer) FAILED! Expected 100, got " .. z)
else
TESTS_PASSED = TESTS_PASSED + 1
end
--============================================================
-- Test 3: Ascending for loop (float)
--============================================================
w = 0
for _=0, 100, 0.1 do
w = w + 0.1
end
if round(w, 1) ~= 100 then
print("TEST 3: Ascending for loop (float) FAILED! Expected 100, got " .. round(w, 1))
else
TESTS_PASSED = TESTS_PASSED + 1
end
--============================================================
-- Test 4: Descending for loop (float)
--============================================================
w = 0
for _=100, 0, -0.1 do
w = w + 0.1
end
if round(w, 1) ~= 100 then
print("TEST 4: Descending for loop (float) FAILED! Expected 100, got " .. round(w, 1))
else
TESTS_PASSED = TESTS_PASSED + 1
end
--============================================================
-- Test 5: Table iteration (ipairs)
--============================================================
t = {1, 2, 3, 4, 5}
x = 0
expected = 20
for _,v in ipairs(t) do
x = x + (1+v)
end
if x ~= expected then
print("TEST 5: Table iteration (ipairs) FAILED! Expected 5, got " .. x)
else
TESTS_PASSED = TESTS_PASSED + 1
end
--============================================================
-- Test 6: Table iteration (pairs)
-- Note: the test is written this way because the pairs function is not sequential.
-- However, numbers when added together are always the same.
--============================================================
expected = 750
t = {a = 1, b = 2, c = 3, d = 4, e = 5}
y = 0
for k, v in pairs(t) do
y = y + str2num(k .. v)
end
if y ~= expected then
print("TEST 6: Table iteration (pairs) FAILED! Expected " .. expected .. ", got " .. y)
else
TESTS_PASSED = TESTS_PASSED + 1
end
print("TESTS PASSED: " .. TESTS_PASSED .. "/6")
================================================
FILE: tests/matrix.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- matrix.lua
--
-- This Test demonstrates a deterministic 2x2 matrix multiplication example.
local function multiply(a, b)
local result = {
{ a[1][1] * b[1][1] + a[1][2] * b[2][1], a[1][1] * b[1][2] + a[1][2] * b[2][2] },
{ a[2][1] * b[1][1] + a[2][2] * b[2][1], a[2][1] * b[1][2] + a[2][2] * b[2][2] }
}
return result
end
local A = {
{1, 2},
{3, 4}
}
local B = {
{5, 6},
{7, 8}
}
local C = multiply(A, B)
for row = 1, 2 do
print(string.format("%d,%d", C[row][1], C[row][2]))
end
================================================
FILE: tests/metatables.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- metatables.lua
--
-- This Test demonstrates a metamethod driven vector arithmetic.
local Vector = {}
Vector.__index = Vector
function Vector:new(x, y)
return setmetatable({ x = x, y = y }, self)
end
function Vector.__add(a, b)
return Vector:new(a.x + b.x, a.y + b.y)
end
function Vector:describe()
return string.format("(%d,%d)", self.x, self.y)
end
local path = {
Vector:new(2, 3),
Vector:new(-1, 4),
Vector:new(0, -2)
}
local position = Vector:new(0, 0)
for idx, delta in ipairs(path) do
position = position + delta
print(string.format("step%d:%s", idx, position:describe()))
end
================================================
FILE: tests/multi-return.lua
================================================
--============================================================
-- Multi-Return Test Suite
-- Target: Compiler
-- Author: SpinnySpiwal
-- Purpose: Ensure multi-return behavior is not adversely affected by the new dynamic emission system.
--============================================================
local function half(number)
local divided = number / 2
return divided, divided
end
local a, b = half(10)
assert(a == 5 and b == 5, "Test 1 failed: basic multi-return")
print("Test 1 passed: basic multi-return", a, b)
local function mixedReturn()
return 42, "hello", true, nil
end
local num, str, bool, nilVal = mixedReturn()
assert(num == 42 and str == "hello" and bool == true and nilVal == nil, "Test 2 failed: mixed types")
print("Test 2 passed: mixed types", num, str, bool, nilVal)
local function threeValues()
return 1, 2, 3
end
local first = threeValues()
assert(first == 1, "Test 3 failed: discarding extra values")
print("Test 3 passed: discarding extra values", first)
local x, y, z, w = threeValues()
assert(x == 1 and y == 2 and z == 3 and w == nil, "Test 4 failed: extra variables get nil")
print("Test 4 passed: extra variables get nil", x, y, z, w)
local function pair()
return "a", "b"
end
local t1 = { pair() }
assert(t1[1] == "a" and t1[2] == "b", "Test 5 failed: multi-return in table (last)")
print("Test 5 passed: multi-return in table (last)", t1[1], t1[2])
local t2 = { pair(), "c" }
assert(t2[1] == "a" and t2[2] == "c" and t2[3] == nil, "Test 6 failed: multi-return not last")
print("Test 6 passed: multi-return not last", t2[1], t2[2])
local function double(a, b)
return a * 2, b * 2
end
local d1, d2 = double(threeValues())
assert(d1 == 2 and d2 == 4, "Test 7 failed: nested multi-return")
print("Test 7 passed: nested multi-return", d1, d2)
local function fiveValues()
return 10, 20, 30, 40, 50
end
local count = select("#", fiveValues())
assert(count == 5, "Test 8 failed: select count")
print("Test 8 passed: select count", count)
local fourth = select(4, fiveValues())
assert(fourth == 40, "Test 9 failed: select specific")
print("Test 9 passed: select specific", fourth)
local function varargReturn(...)
return ...
end
local v1, v2, v3 = varargReturn(100, 200, 300)
assert(v1 == 100 and v2 == 200 and v3 == 300, "Test 10 failed: vararg return")
print("Test 10 passed: vararg return", v1, v2, v3)
local function sum(a, b, c)
return (a or 0) + (b or 0) + (c or 0)
end
local result = sum(threeValues())
assert(result == 6, "Test 11 failed: multi-return as arguments")
print("Test 11 passed: multi-return as arguments", result)
print("All multi-return tests passed!")
================================================
FILE: tests/primes.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- primes.lua
--
-- This Test demonstrates a deterministic prime number generator.
local function primes(n)
local function isPrime(n)
for i = 2, math.sqrt(n) do
if n % i == 0 then
return false
end
end
return true
end
for i = 2, n do
if isPrime(i) then
print(i)
end
end
end
primes(20)
================================================
FILE: tests/repeat-test.lua
================================================
--============================================================
-- Repeat-Until Semantics Test Suite
-- Target: Vmify
-- Author: Zaenalos
-- Purpose: Validate correct scope, control flow, and condition
--============================================================
local TEST_ID = 0
local function test(name, fn)
TEST_ID = TEST_ID + 1
local ok, err = pcall(fn)
if not ok then
error(string.format(
"[FAIL] #%d %s\n → %s",
TEST_ID, name, err
), 2)
end
print(string.format("[PASS] #%d %s", TEST_ID, name))
end
--============================================================
-- Test 1: Basic repeat-until with local in condition scope
--============================================================
test("Basic local visibility in until condition", function()
local count = 0
repeat
local x = count
count = count + 1
until x == 5
assert(count == 6, "count should be 6")
end)
--============================================================
-- Test 2: Locals do not leak outside repeat scope
--============================================================
test("Repeat locals do not escape scope", function()
repeat
local hidden = 123
until true
assert(_G.hidden == nil, "local leaked into global scope")
end)
--============================================================
-- Test 3: Immediate exit still executes body once
--============================================================
test("Immediate termination executes once", function()
local iters = 0
repeat
iters = iters + 1
until true
assert(iters == 1, "repeat body must run exactly once")
end)
--============================================================
-- Test 4: Multiple locals and arithmetic correctness
--============================================================
test("Multiple locals and arithmetic", function()
local i = 0
local c
repeat
local a = i
local b = a * 2
c = a + b
i = i + 1
until c >= 15
assert(i == 6, "i should be 6 when c reaches 15")
end)
--============================================================
-- Test 5: Nested repeat-until with independent scopes
--============================================================
test("Nested repeat loops", function()
local outer = 0
local total_inner = 0
repeat
local inner = 0
repeat
total_inner = total_inner + 1
inner = inner + 1
until inner == 3
outer = outer + 1
until outer == 3
assert(outer == 3, "outer loop count mismatch")
assert(total_inner == 9, "inner loop count mismatch")
end)
--============================================================
-- Test 6: Function call inside until condition
--============================================================
test("Function call in until condition", function()
local function check(x)
return x >= 3
end
local k = 0
local current
repeat
current = k
k = k + 1
until check(current)
assert(k == 4, "termination point incorrect")
end)
--============================================================
-- Test 7: Upvalue capture inside repeat
--============================================================
test("Upvalue capture from repeat body", function()
local f
repeat
local x = 42
f = function()
return x
end
until true
assert(f() == 42, "upvalue incorrectly captured")
end)
--============================================================
-- Test 8: Side effects inside until condition (corrected)
--============================================================
test("Side effects in until condition", function()
local log = {}
local i = 0
repeat
i = i + 1
until (function()
log[#log + 1] = i -- explicit side effect
return i >= 3
end)()
assert(#log == 3, "side effects count mismatch")
assert(log[3] == 3, "final side effect value incorrect")
end)
--============================================================
-- Test 9: Break skips until condition
--============================================================
test("Break bypasses until evaluation", function()
local evaluated = false
repeat
break
until (function()
evaluated = true
return true
end)()
assert(evaluated == false, "`until` condition evaluated after break")
end)
--============================================================
-- Test 10: Slot reuse and shadowing correctness
--============================================================
test("Local shadowing and slot reuse", function()
local sum = 0
local i = 0
repeat
local v = i
sum = sum + v
do
local v = v * 2
sum = sum + v
end
i = i + 1
until i == 3
assert(sum == (0+0) + (1+2) + (2+4), "slot corruption detected")
end)
--============================================================
-- Test 11: Table mutation inside repeat
--============================================================
test("Table writes inside repeat", function()
local t = {}
local i = 1
repeat
t[i] = i * i
i = i + 1
until i > 5
assert(#t == 5, "table size incorrect")
assert(t[5] == 25, "table content incorrect")
end)
--============================================================
-- Test 12: Deterministic non-linear termination
--============================================================
test("Non-linear termination logic", function()
local x = 1
repeat
x = (x * 3 + 1) % 17
until x == 0
assert(x == 0, "non-linear termination failed")
end)
--============================================================
print("\nAll repeat-until tests passed successfully.")
================================================
FILE: tests/state-machine.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- state-machine.lua
--
-- This Test demonstrates a simple deterministic finite-state machine.
local transitions = {
idle = { start = "running" },
running = { pause = "paused", stop = "stopped" },
paused = { resume = "running", stop = "stopped" }
}
local steps = {
{ event = "start", expect = "running" },
{ event = "pause", expect = "paused" },
{ event = "resume", expect = "running" },
{ event = "stop", expect = "stopped" }
}
local state = "idle"
for idx, step in ipairs(steps) do
local rule = transitions[state]
state = rule and rule[step.event]
assert(state == step.expect, string.format("bad transition at %d", idx))
print(string.format("%d:%s->%s", idx, step.event, state))
end
print("final:" .. state)
================================================
FILE: tests/strings.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- strings.lua
--
-- This Test demonstrates a deterministic text statistics for repeated words and prints the results.
local passage = "lorem ipsum dolor sit amet ipsum lorem"
local counts = {}
for word in passage:gmatch("%w+") do
counts[word] = (counts[word] or 0) + 1
end
local order = {"lorem", "ipsum", "dolor", "sit", "amet"}
for _, word in ipairs(order) do
print(string.format("%s:%d", word, counts[word] or 0))
end
================================================
FILE: tests/syntax.lua
================================================
--============================================================
-- Syntax Test Suite
-- Target: Unparser
-- Author: SpinnySpiwal
-- Purpose: Validate appropriate parser & unparser functionality, specifically in unseen edge cases.
-- Update 1: Added test for precedence bug fix in expressionPow.
--============================================================
local char = ("").char
print(char == string.char and "yes" or "no")
local pc, _ = pcall(function()
return (0).char
end)
-- Checks for unparser bug
print(pc == false and "yes" or "no")
local ok = pcall(function(...)
print("hello " .. ...)
end)
print(ok and "no" or "yes")
local function getString()
return "this string is 24 chars!"
end
-- Test for precedence bug fix in expressionPow
if 2 ^ #getString() == 16777216 then
print("TEST 1 PASSED")
else
print("TEST 1 FAILED")
end
-- Check if it still works the other way around
if (#getString()) ^ 2 == 576 then
print("TEST 2 PASSED")
else
print("TEST 2 FAILED")
end
================================================
FILE: tests/table-merge.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- table-merge.lua
--
-- This Test demonstrates a deterministic table merging and traversal.
local breakfast = { eggs = 4, bacon = 3 }
local lunch = { bacon = 1, toast = 5 }
local function mergeQuantities(a, b)
local totals = {}
for k, v in pairs(a) do
totals[k] = v
end
for k, v in pairs(b) do
totals[k] = (totals[k] or 0) + v
end
return totals
end
local merged = mergeQuantities(breakfast, lunch)
local order = {"eggs", "bacon", "toast"}
for _, item in ipairs(order) do
print(string.format("%s:%d", item, merged[item] or 0))
end
================================================
FILE: tests/upvalues.lua
================================================
-- This Test is Part of the Prometheus Obfuscator by Levno_710
--
-- upvalues.lua
--
-- This Test demonstrates a deterministic tests covering closure upvalues in nested functions and loops.
local function emitList(label, list)
print(label .. ":" .. table.concat(list, ","))
end
local function makeSeries(tag)
local total = 0
local function step(delta)
total = total + delta
return string.format("%s-%d", tag, total)
end
local function runSeries(values)
local out = {}
for _, delta in ipairs(values) do
out[#out + 1] = step(delta)
end
return out
end
return runSeries
end
local alphaSeries = makeSeries("alpha")
emitList("series", alphaSeries({ 1, 2, 1, 3 }))
-- Verify each for-loop iteration captures its own upvalue
local watchers = {}
for i = 1, 4 do
watchers[i] = function(mult)
return i * mult
end
end
for idx, fn in ipairs(watchers) do
print(string.format("watch%d:%d", idx, fn(idx + 1)))
end
-- Nested functions sharing a master accumulator through for-loops
local function buildAccumulators()
local master = 0
local store = {}
for group = 1, 3 do
local localTotal = group
store[group] = function(iterations)
for step = 1, iterations do
localTotal = localTotal + group + step
master = master + group
end
return localTotal, master
end
end
return store
end
local runners = buildAccumulators()
for idx, fn in ipairs(runners) do
local value, master = fn(idx)
print(string.format("acc%d:%d|%d", idx, value, master))
end
================================================
FILE: tests.lua
================================================
-- This Script is Part of the Prometheus Obfuscator by Levno_710
--
-- tests.lua
--
-- This Script will Perform tests using all lua files within the tests directory
-- Require Prometheus
local Prometheus = require("src.prometheus")
-- Enable Debugging
-- logger.logLevel = logger.LogLevel.Debug;
-- Config Variables - Later passed as Parameters
local noColors = false; -- Whether Colors in the Console output should be enabled
local isWindows = package.config:sub(1, 1) == "\\"; -- Whether the Test are Performed on a Windows or Linux System
local ciMode = false; -- Whether the Test error are ignored or not
local iterationCount = 20; -- How often each test should be executed
for _, currArg in pairs(arg) do
if currArg == "--Linux" then
isWindows = false
end
if currArg == "--Windows" then
isWindows = true
end
if currArg == "--CI" then
ciMode = true
end
local iterationValue = currArg:match("^%-%-iterations=(%d+)$")
if iterationValue then
iterationCount = math.max(tonumber(iterationValue), 1)
end
end
-- Enable/Disable Console Colors - this may be needed because cmd.exe and powershell.exe do not support ANSI Color Escape Sequences. The Windows Terminal Application is needed
Prometheus.colors.enabled = not noColors;
-- Apply Obfuscation Pipeline
local pipeline = Prometheus.Pipeline:new({
Seed = 0; -- For Using Time as Seed
VarNamePrefix = ""; -- No Custom Prefix
});
-- "Mangled" for names like this : a, b, c, d, ...
-- "MangledShuffled" is the same except the chars come in a different order - Recommended
-- "Il" for weird names like this : IlIIl1llI11l1 - Recommended to make less readable
-- "Number" for names like this : _1, _2, _3, ... - Not recommended
pipeline:setNameGenerator("MangledShuffled");
local function describePlatform()
return isWindows and "Windows" or "Linux"
end
print(string.format(
"Performing Prometheus Tests (iterations=%d per file/preset, platform=%s)...",
iterationCount,
describePlatform()
))
local function scandir(directory)
local i, t, popen = 0, {}, io.popen
local pfile = popen(isWindows and 'dir "'..directory..'" /b' or 'ls -a "'..directory..'"')
if not pfile then
error("Failed to list files in test directory: " .. tostring(directory))
end
for filename in pfile:lines() do
if string.sub(filename, -4) == ".lua" then
i = i + 1
t[i] = filename
end
end
pfile:close()
return t
end
local function shallowcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
local function validate(a, b)
local outa = "";
local outb = "";
local enva = shallowcopy(getfenv(a));
local envb = shallowcopy(getfenv(a));
enva.print = function(...)
for _, v in ipairs({...}) do
outa = outa .. tostring(v);
end
end
envb.print = function(...)
for _, v in ipairs({...}) do
outb = outb .. tostring(v);
end
end
setfenv(a, enva);
setfenv(b, envb);
if(not pcall(a)) then error("Expected Reference Program not to Fail!") end
if(not pcall(b)) then return false, outa, nil end
return outa == outb, outa, outb
end
local presets = Prometheus.Presets;
local testdir = "./tests/"
Prometheus.Logger.logLevel = Prometheus.Logger.LogLevel.Error;
local fc = 0;
for _, filename in ipairs(scandir(testdir)) do
local path = testdir .. filename;
local file = io.open(path,"r");
local code = file:read("*a");
print(Prometheus.colors("[CURRENT] ", "magenta") .. filename);
for name, preset in pairs(presets) do
for i = #preset.Steps, 1, -1 do
if preset.Steps[i].Name == "AntiTamper" then
table.remove(preset.Steps, i);
end
end
for _ = 1, iterationCount do
pipeline = Prometheus.Pipeline:fromConfig(preset);
local obfuscated = pipeline:apply(code);
local funca = loadstring(code);
local funcb = loadstring(obfuscated);
if funcb == nil then
print(Prometheus.colors("[FAILED] ", "red") .. "(" .. filename .. "): " .. name .. ", Invalid Lua!");
print("[SOURCE]", obfuscated);
fc = fc + 1;
else
local validated, outa, outb = validate(funca, funcb);
if not validated then
print(Prometheus.colors("[FAILED] ", "red") .. "(" .. filename .. "): " .. name);
print("[OUTA] ", outa);
print("[OUTB] ", outb);
print("[SOURCE]", obfuscated);
fc = fc + 1;
end
end
end
end
file:close();
end
if fc < 1 then
print(Prometheus.colors("[PASSED] ", "green") .. "All tests passed!");
return 0;
else
print(Prometheus.colors("[FAILED] ", "red") .. "Some tests failed!");
if ciMode then
error("Test Failed!")
end
return -1;
end