You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
9.3 KiB
393 lines
9.3 KiB
local confuser = require 'optimizer.confuser'
|
|
|
|
local ipairs = ipairs
|
|
local pairs = pairs
|
|
|
|
local jass, report, confuse1, confuse2
|
|
local current_function, current_line, has_call
|
|
local executes, executed_any
|
|
local mark_exp, mark_lines, mark_function
|
|
|
|
local function get_function(name)
|
|
return jass.functions[name]
|
|
end
|
|
|
|
local function get_arg(name)
|
|
if current_function and current_function.args then
|
|
return current_function.args[name]
|
|
end
|
|
end
|
|
|
|
local function get_local(name)
|
|
if current_function then
|
|
local locals = current_function.locals
|
|
if locals then
|
|
for i = #locals, 1, -1 do
|
|
local loc = locals[i]
|
|
if loc.name == name and loc.line < current_line then
|
|
return loc
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function get_global(name)
|
|
return jass.globals[name]
|
|
end
|
|
|
|
local function get_var(name)
|
|
local var = get_local(name)
|
|
if var then
|
|
return var, 'local'
|
|
end
|
|
local var = get_arg(name)
|
|
if var then
|
|
return var, 'arg'
|
|
end
|
|
local var = get_global(name)
|
|
if var then
|
|
return var, 'global'
|
|
end
|
|
end
|
|
|
|
local function mark_var(var)
|
|
local use_var, type = get_var(var.name)
|
|
if type == 'global' and use_var.file ~= 'war3map.j' then
|
|
return
|
|
end
|
|
use_var.used = true
|
|
if confuse1 then
|
|
use_var.confused = confuse1(var.name)
|
|
end
|
|
end
|
|
|
|
function mark_exp(exp)
|
|
if not exp then
|
|
return
|
|
end
|
|
if exp.type == 'null' or exp.type == 'integer' or exp.type == 'real' or exp.type == 'string' or exp.type == 'boolean' then
|
|
elseif exp.type == 'var' or exp.type == 'vari' then
|
|
mark_var(exp)
|
|
elseif exp.type == 'call' then
|
|
has_call = true
|
|
mark_function(exp)
|
|
elseif exp.type == 'code' then
|
|
mark_function(exp)
|
|
end
|
|
for i = 1, #exp do
|
|
mark_exp(exp[i])
|
|
end
|
|
end
|
|
|
|
local function mark_locals(locals)
|
|
for _, loc in ipairs(locals) do
|
|
if loc[1] then
|
|
current_line = loc.line
|
|
has_call = false
|
|
mark_exp(loc[1])
|
|
if has_call then
|
|
loc.used = true
|
|
if confuse1 then
|
|
loc.confused = confuse1(loc.name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function mark_execute(line)
|
|
if not executes then
|
|
executes = {}
|
|
end
|
|
local exp = line[1]
|
|
if exp.type == 'string' then
|
|
if get_function(exp.value) then
|
|
mark_function(get_function(exp.value))
|
|
end
|
|
return
|
|
end
|
|
if exp.type == '+' then
|
|
if exp[1].type == 'string' then
|
|
local head = exp[1].value
|
|
executes[head] = true
|
|
report('引用函数', ('%s...'):format(head), ('第[%d]行:ExecuteFunc("%s" + ...)'):format(line.line, head))
|
|
return
|
|
end
|
|
end
|
|
if not executed_any then
|
|
executed_any = true
|
|
report('强制引用全部函数', '强制引用全部函数', ('第[%d]行:完全动态的ExecuteFunc'):format(line.line))
|
|
if confuse1 then
|
|
report('没有混淆函数名', '没有混淆函数名', ('第[%d]行:完全动态的ExecuteFunc'):format(line.line))
|
|
end
|
|
end
|
|
end
|
|
|
|
local function mark_call(line)
|
|
mark_function(line)
|
|
for _, exp in ipairs(line) do
|
|
mark_exp(exp)
|
|
end
|
|
if line.name == 'ExecuteFunc' then
|
|
mark_execute(line)
|
|
end
|
|
end
|
|
|
|
local function mark_set(line)
|
|
mark_var(line)
|
|
mark_exp(line[1])
|
|
end
|
|
|
|
local function mark_seti(line)
|
|
mark_var(line)
|
|
mark_exp(line[1])
|
|
mark_exp(line[2])
|
|
end
|
|
|
|
local function mark_return(line)
|
|
if line[1] then
|
|
mark_exp(line[1])
|
|
end
|
|
end
|
|
|
|
local function mark_exit(line)
|
|
mark_exp(line[1])
|
|
end
|
|
|
|
local function mark_if(data)
|
|
mark_exp(data.condition)
|
|
mark_lines(data)
|
|
end
|
|
|
|
local function mark_elseif(data)
|
|
mark_exp(data.condition)
|
|
mark_lines(data)
|
|
end
|
|
|
|
local function mark_else(data)
|
|
mark_lines(data)
|
|
end
|
|
|
|
local function mark_ifs(chunk)
|
|
for _, data in ipairs(chunk) do
|
|
if data.type == 'if' then
|
|
mark_if(data)
|
|
elseif data.type == 'elseif' then
|
|
mark_elseif(data)
|
|
else
|
|
mark_else(data)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function mark_loop(chunk)
|
|
mark_lines(chunk)
|
|
end
|
|
|
|
function mark_lines(lines)
|
|
for _, line in ipairs(lines) do
|
|
current_line = line.line
|
|
if line.type == 'call' then
|
|
mark_call(line)
|
|
elseif line.type == 'set' then
|
|
mark_set(line)
|
|
elseif line.type == 'seti' then
|
|
mark_seti(line)
|
|
elseif line.type == 'return' then
|
|
mark_return(line)
|
|
elseif line.type == 'exit' then
|
|
mark_exit(line)
|
|
elseif line.type == 'if' then
|
|
mark_ifs(line)
|
|
elseif line.type == 'loop' then
|
|
mark_loop(line)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function mark_takes(args)
|
|
if not args then
|
|
return
|
|
end
|
|
for _, arg in ipairs(args) do
|
|
if confuse1 then
|
|
arg.confused = confuse1(arg.name)
|
|
end
|
|
end
|
|
end
|
|
|
|
function mark_function(call)
|
|
if not call then
|
|
return
|
|
end
|
|
local func = get_function(call.name)
|
|
if func.native then
|
|
func.used = true
|
|
return
|
|
end
|
|
if func.used or func.file ~= 'war3map.j' then
|
|
return
|
|
end
|
|
func.used = true
|
|
local _current_function = current_function
|
|
local _current_line = current_line
|
|
current_function = func
|
|
mark_takes(func.args)
|
|
mark_locals(func.locals)
|
|
mark_lines(func)
|
|
current_function = _current_function
|
|
current_line = _current_line
|
|
end
|
|
|
|
local function mark_globals()
|
|
for _, global in ipairs(jass.globals) do
|
|
if global[1] then
|
|
current_line = global.line
|
|
has_call = false
|
|
mark_exp(global[1])
|
|
if has_call then
|
|
global.used = true
|
|
if confuse1 then
|
|
global.confused = confuse1(global.name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function mark_executed_used(func)
|
|
if func.used then
|
|
return
|
|
end
|
|
if executed_any then
|
|
mark_function(func)
|
|
return
|
|
end
|
|
local name = func.name
|
|
for head in pairs(executes) do
|
|
if name:sub(1, #head) == head then
|
|
mark_function(func)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local function mark_executed_confuse(func)
|
|
local name = func.name
|
|
if name == 'config' or name == 'main' then
|
|
return
|
|
end
|
|
if func.native then
|
|
return
|
|
end
|
|
for head in pairs(executes) do
|
|
if name:sub(1, #head) == head then
|
|
func.confused = confuse2(head) .. name:sub(#head+1)
|
|
jass.confused_head[head] = confuse2(head)
|
|
return
|
|
end
|
|
end
|
|
func.confused = confuse1(name)
|
|
end
|
|
|
|
local function mark_executed()
|
|
if not executes then
|
|
return
|
|
end
|
|
for _, func in ipairs(jass.functions) do
|
|
mark_executed_used(func)
|
|
end
|
|
if not executed_any and confuse1 then
|
|
for _, func in ipairs(jass.functions) do
|
|
mark_executed_confuse(func)
|
|
end
|
|
end
|
|
end
|
|
|
|
local cant_use = {'globals', 'endglobals', 'constant', 'native', 'array', 'and', 'or', 'not', 'type', 'extends', 'function', 'endfunction', 'nothing', 'takes', 'returns', 'call', 'set', 'return', 'if', 'endif', 'elseif', 'else', 'loop', 'endloop', 'exitwhen', 'main', 'config'}
|
|
for _, name in ipairs(cant_use) do
|
|
cant_use[name] = true
|
|
end
|
|
|
|
local function can_use(name)
|
|
if cant_use[name] then
|
|
return false
|
|
end
|
|
local func = get_function(name)
|
|
if func then
|
|
if func.file ~= 'war3map.j' then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
local var, type = get_var(name)
|
|
if type == 'global' then
|
|
if var.file ~= 'war3map.j' then
|
|
return false
|
|
end
|
|
return true
|
|
elseif type == 'arg' or type == 'local' then
|
|
return true
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function init_confuser(confusion)
|
|
if not confusion then
|
|
return
|
|
end
|
|
|
|
confusion = tostring(confusion)
|
|
local chars = {}
|
|
for char in confusion:gmatch '[%w_]' do
|
|
if not chars[char] then
|
|
chars[#chars+1] = char
|
|
end
|
|
end
|
|
if #chars < 3 then
|
|
report('脚本混淆失败', '脚本混淆失败', '至少要有3个合法字符')
|
|
end
|
|
|
|
confusion = table.concat(chars)
|
|
|
|
for char in confusion:gmatch '%a' do
|
|
chars[#chars+1] = char
|
|
end
|
|
if #chars < 2 then
|
|
report('脚本混淆失败', '脚本混淆失败', '至少要有2个字母')
|
|
return
|
|
end
|
|
|
|
local confuse_head = chars[1]
|
|
confuse1 = confuser(confusion:gsub(confuse_head, ''))
|
|
function confuse1:on_find(name)
|
|
if can_use(name) then
|
|
return name
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
jass.confused_head = {}
|
|
confuse2 = confuser(confusion)
|
|
function confuse2:on_find(name)
|
|
name = confuse_head .. name
|
|
if can_use(name) then
|
|
return name
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
end
|
|
|
|
return function (ast, config, _report)
|
|
jass = ast
|
|
report = _report
|
|
|
|
init_confuser(config.confusion)
|
|
mark_globals()
|
|
mark_function(get_function 'config')
|
|
mark_function(get_function 'main')
|
|
mark_executed()
|
|
end
|
|
|