194 lines
6.0 KiB
Lua
194 lines
6.0 KiB
Lua
-- 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 j<jmin then -- pick closest one
|
|
jmin = j
|
|
mode = k
|
|
end
|
|
end
|
|
if mode ~= 0 then -- found something
|
|
j=jmin
|
|
ret[#ret+1] = {jmin}
|
|
end
|
|
if not j then break end -- found nothing
|
|
else
|
|
_,j = string.find(code,modes[mode][2],i); -- search for closing pair
|
|
if not j then break end
|
|
if (mode~=2 or (string.sub(code,j-1,j-1) ~= "\\") or string.sub(code,j-2,j-1) == "\\\\") then -- not (" and not \" - but "\\" is allowed)
|
|
ret[#ret][2] = j
|
|
mode = 0
|
|
end
|
|
end
|
|
i=j -- move to next position
|
|
end
|
|
if mode~= 0 then ret[#ret][2] = length end
|
|
return ret
|
|
end
|
|
|
|
local is_inside_string = function(strings,pos) -- is position inside one of the strings?
|
|
local low = 1; local high = #strings;
|
|
if high == 0 then return false end
|
|
local mid = high;
|
|
while high>low+1 do
|
|
mid = math.floor((low+high)/2)
|
|
if pos<strings[mid][1] then high = mid else low = mid end
|
|
end
|
|
if pos>strings[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 |