SoUI 0.5版本占坑
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

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