-- 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;