-- LUA OBFUSCATOR by rnd -- removes all newlines, tabs and unneded spaces -- renames all local variables to short form local reserved_words = { ["for"] = true, ["else"] =true, ["if"] = true, ["then"] = true, ["not"] = true, ["end"] = true, ["in"] = true, ["return"] = true, ["or"] = true, ["and"] = true, ["local"] = true,["function"] = true,["string"] = true, ["break"]=true, ["elseif"] = true, ["false"] = true, ["nil"] = true, ["repeat"] = true, ["while"] = true, ["true"]=true, } local remove_comments = function(text) return text:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") end local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code local i = 0; local j; local _; local length = string.len(code); local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string local modes = { {"'","'"}, -- inside ' ' {"\"","\""}, -- inside " " {"%[=*%[","%]=*%]"}, -- inside [=[ ]=] } local ret = {} while i < length do i=i+1 local jmin = length+1; if mode == 0 then -- not yet inside string for k=1,#modes do j = string.find(code,modes[k][1],i); if j and jlow+1 do mid = math.floor((low+high)/2) if posstrings[low][2] then mid = high else mid = low end return strings[mid][1]<=pos and pos<=strings[mid][2] end local find_outside_string = function(text, pattern, pos, strings) local length = string.len(text) local found = true; local i1 = pos; while found do found = false local i2 = string.find(text,pattern,i1); if i2 then if not is_inside_string(strings,i2) then return i2 end found = true; i1 = i2+1; end end return nil end local extract_locals = function(text,locals, positions) local strings = identify_strings(text); local i = 1; local length = string.len(text); local idx = 0 while(i<=length) do local j1 = find_outside_string(text,"[%a_]+",i, strings) if j1 then -- find locals (local variables) local j2 = string.find(text,"[^%a_]",j1+1) or (length+1) local word = string.sub(text,j1,j2-1) i=j2+1 if not reserved_words[word] then if not locals[word] and string.sub(text,j1-6,j1-2) == "local" then -- not yet found as local and "local variable_name" idx=idx+1;locals[word] = idx -- found new local end end else break end end i=1 while(i<=length) do -- find locals positions (all of them!) local j1 = find_outside_string(text,"[%a_]+",i, strings) if j1 then -- find locals (local variables) local j2 = string.find(text,"[^%a_]",j1+1) or (length+1) local word = string.sub(text,j1,j2-1) i=j2+1 if locals[word] then positions[#positions+1] = {j1,j2,locals[word]} -- remember positions and local end else break end end end local rename_locals = function(text,locals,positions) local i = 1; local out = {}; local chars = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","z","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","Z","W","X","Y","Z"} local names = {}; local j = 0 local n = #chars; for _,__ in pairs(locals) do -- generate new local names j=j+1 if j<=n then names[j] = chars[j] elseif j<=n^2 then names[j] = chars[math.floor(j/n)] .. chars[(j-1)%n+1] else print("error! count exceeded " .. n^2) -- default 54^2=2916 end end for j = 1,#positions do out[#out+1] = string.sub(text,i,positions[j][1]-1)..names[positions[j][3]] i=positions[j][2] end out[#out+1] = string.sub(text,i,string.len(text)) return table.concat(out,"") end local obfuscate = function(text) text = remove_comments(text); text = string.gsub(text,"[\n\t ]+"," ") -- replace newlines, tabs and multiple spaces with space local locals = {}; local positions = {};extract_locals(text,locals, positions) --print(serialize(locals));print(serialize(positions)) return rename_locals(text,locals,positions) -- this is final output end local text; local pattern; local code; local pos; local tab -- just so it gets obfuscated too when applied to itself -- DEMO EXAMPLE -- only variables that are local somewhere are obfuscated, like 'local variable_name' -- possible issues: table = {x=1}; local x; res["x"] -- here x will get renamed, but x in res["x"] not producing possible error, however res.x will be renamed correctly text = [=[ local i; local n local factors = function( n ) local f = {} for i = 1, n/2 do -- here we try all the possible factors of n if n % i == 0 then -- here we check if n is divisible by i f[#f+1] = i -- we found factor, add it to the list end end f[#f+1] = n print("factors of " .. n .. " are : " .. table.concat(f,",")) end factors(25) ]=] --[[ obfuscated version is: local a; local b local c = function( b ) local d = {} for a = 1, b/2 do if b % a == 0 then d[#d+1] = a end end d[#d+1] = b print("factors of " .. b .. " are : " .. table.concat(d,",")) end c(25) --]] print(obfuscate(text)) -- print obfuscated version