[ad_1]
Created by ???????
Features:
--[[
wild_west_deobfuscator.lua
made in about ~4 hours releasing this cus im mad and wild west owner refuses to pay me
so enjoy updating ur shitty wild west exploits
learn how to use this urself im not going to spoonfeed anyone
LgouCi4KLgouCgozMDYxMjc4NTQ2NTgyNTY4OTYK
]]
-- stil not fully finished
local deobfuscation_settings = {
enable_lower_optimizations = true;
}
-- utilities
local bit = require('bit32');
-- bytecode stream
local vanilla_dumper = { };
vanilla_dumper.sizet_size = 4;
vanilla_dumper.int_size = 4;
local bytecode_stream = { };
do
bytecode_stream.__index = bytecode_stream;
bytecode_stream.__tostring = function(self)
return table.concat(self.buffer_);
end
function bytecode_stream.new()
return setmetatable({
buffer_ = { };
}, bytecode_stream);
end
function bytecode_stream:write_int(number, int_size) -- big endian recursive order
int_size = int_size or vanilla_dumper.int_size;
self:write_byte(bit.band(number, 0xFF));
if (int_size == 1) then
return;
end
self:write_int(bit.rshift(number, 8), int_size - 1);
return self;
end
function bytecode_stream:write_byte(byte)
table.insert(self.buffer_, string.char(byte));
return self;
end
local function mantissa_to_bytes(m)
-- 54 bits
-- floor(2^k * m) = 2^k m_53 + ... + m_{53 - k}
-- 0 - 31 (32 bits), 32 - 54 (23 bits)
local hi = math.floor(0x100000 * m)
local lo = 0x100000 * m - hi
lo = math.floor(lo * 0x100000000)
hi = bit.band(0xfffff, hi)
return lo, hi
end
function bytecode_stream:write_double(number)
if number == 0 then self:write_int(0, 8) return end
local m, e = math.frexp(number)
m = 2*m
e = e - 1
local lo, hi_m = mantissa_to_bytes(m)
-- 1 11 52
-- hi:63 - sign
-- hi:62-52 - exp
-- hilo - 0 - man
-- HI LO
-- 00000000000000000000000000000000 00000000000000000000000000000000
-- Seeeeeeeeeeemmmmmmmmmmmmmmmmmmmm mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
local hi_e = bit.lshift(bit.band(0x7ff, e + 1023), 20)
local sign = bit.lshift(number < 0 and 1 or 0, 31)
local hi = bit.bor(sign, bit.bor(hi_m, hi_e))
self:write_int(lo, 4)
self:write_int(hi, 4)
end
function bytecode_stream:write_string(str, sizet_size)
sizet_size = sizet_size or vanilla_dumper.sizet_size;
self:write_int(#str + 1, sizet_size);
for i = 1, #str do
self:write_byte(str:byte(i));
end
self:write_byte(0);
return self;
end
end
do
function vanilla_dumper.dump_header(writer)
writer:write_int(0x61754C1B)
:write_byte(0x51)
:write_byte(0)
:write_byte(1)
:write_byte(vanilla_dumper.int_size)
:write_byte(vanilla_dumper.sizet_size)
:write_byte(4)
:write_byte(8) -- lua_Number default 8
:write_byte(0);
end
function vanilla_dumper.dump_chunk(writer, chunk)
local function write_code()
writer:write_int(chunk.size_code);
for i = 1, chunk.size_code do
local instruction = chunk.code[i];
local data = 0;
data = bit.bor(data, instruction.opcode);
if (instruction.type == 'AsBx') then
data = bit.bor(data, bit.lshift(bit.band(instruction.a, 0xff), 6));
data = bit.bor(data, bit.lshift(instruction.sbx + 131071, 14));
elseif (instruction.type == 'ABC') then
data = bit.bor(data, bit.lshift(bit.band(instruction.a, 0xff), 6));
data = bit.bor(data, bit.lshift(bit.band(instruction.c, 0x1ff), 6 + 8));
data = bit.bor(data, bit.lshift(bit.band(instruction.b, 0x1ff), 6 + 8 + 9));
elseif (instruction.type == 'ABx') then
data = bit.bor(data, bit.lshift(bit.band(instruction.a, 0xff), 6));
data = bit.bor(data, bit.lshift(instruction.bx, 14));
end
writer:write_int(data);
end
end
local function write_constants()
writer:write_int(chunk.sizek);
for i = 1, chunk.sizek do
local value = chunk.constants[i];
local value_tt = type(value);
if (value_tt == 'string') then
writer:write_byte(4);
writer:write_string(value);
elseif (value_tt == 'boolean') then
writer:write_byte(1);
writer:write_byte(value and 1 or 0);
elseif (value_tt == 'number') then
writer:write_byte(3);
writer:write_double(value);
else
writer:write_byte(0);
end
end
end
local function write_protos()
writer:write_int(chunk.sizep);
for i = 1, chunk.sizep do
vanilla_dumper.dump_chunk(writer, chunk.protos[i]);
end
end
local function write_debug()
-- TODO
--[[if (chunk.size_lineinfo ~= 0) then
writer:write_int(chunk.size_code);
for i = 1, chunk.size_code do
writer:write_int(chunk.code[i].line);
end
end]]
writer:write_int(0);
--[[writer:write_int(chunk.size_locvars);
for i = 1, chunk.size_locvars do
local locvar = chunk.locvars[i];
writer:write_string(locvar.name);
writer:write_int(locvar.start_pc);
writer:write_int(locvar.end_pc);
end]]
writer:write_int(0);
--[[writer:write_int(chunk.size_upvalues);
for i = 1, chunk.size_upvalues do
writer:write_string(chunk.upvalue_names[i]);
end]]
writer:write_int(0);
end
if (vanilla_dumper.is_top == true) then
writer:write_string(chunk.name or 'empty');
vanilla_dumper.is_top = false;
else
writer:write_string('');
end
writer:write_int(chunk.line_defined);
writer:write_int(chunk.last_line_defined);
writer:write_byte(chunk.nups);
writer:write_byte(chunk.num_parameters);
writer:write_byte(chunk.vararg_flag);
writer:write_byte(chunk.max_stack_size);
write_code();
write_constants();
write_protos();
write_debug();
end
function vanilla_dumper.dump(chunk)
local writer = bytecode_stream.new();
vanilla_dumper.is_top = true;
vanilla_dumper.dump_header(writer);
vanilla_dumper.dump_chunk(writer, chunk);
return tostring(writer);
end
end
local function unpack_bits(Bit, Start, End) -- No tail-calls, yay.
if End then -- Thanks to cntkillme for giving input on this shorter, better approach.
local Res = (Bit / 2 ^ (Start - 1)) % 2 ^ ((End - 1) - (Start - 1) + 1);
return Res - Res % 1;
else
local Plc = 2 ^ (Start - 1);
if (Bit % (Plc + Plc) >= Plc) then
return 1;
else
return 0;
end;
end;
end;
local west_openum = {
"MOVE",
"LOADK",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"GETGLOBAL",
"GETTABLE",
"SETGLOBAL",
"SETUPVAL",
"SETTABLE",
"NEWTABLE",
"SELF",
"ADD",
"SUB",
"MUL",
"DIV",
"MOD",
"POW",
"UNM",
"NOT",
"LEN",
"CONCAT",
"JMP",
"EQ",
"LT",
"LE",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"FORLOOP",
"FORPREP",
"TFORLOOP",
"SETLIST",
"CLOSE",
"CLOSURE",
"VARARG",
"LOADSERVICE",
[64] = 'NOP'; -- 63
};
local west_optype = {
'ABC', 'ABx', 'ABC', 'ABC';
'ABC', 'ABx', 'ABC', 'ABx';
'ABC', 'ABC', 'ABC', 'ABC';
'ABC', 'ABC', 'ABC', 'ABC';
'ABC', 'ABC', 'ABC', 'ABC';
'ABC', 'ABC', 'AsBx', 'ABC';
'ABC', 'ABC', 'ABC', 'ABC';
'ABC', 'ABC', 'ABC', 'AsBx';
'AsBx', 'ABC', 'ABC', 'ABC';
'ABx', 'ABC',
'ABx', -- LOADSERVICE
[64] = 'ABC'; -- NOP
};
local west_undump;
do
local west_signature = ' 27Lua';
function west_undump(bytecode)
local stream_position = 1;
local read_sizet, read_int;
local read1_function, read2_function;
local function read_dword()
local w, x, y, z = string.byte(bytecode, stream_position, stream_position + 3);
stream_position = stream_position + 4;
return (w * 16777216) + (x * 65536) + (y * 256) + z;
end
local function read_string(length)
local result = string.sub(bytecode, stream_position, stream_position + length - 1);
stream_position = stream_position + length;
return result;
end
-- todo big endian readdouble so we dont need this scuffed bs
local function read_double()
local u9 = math.pow(2, 52);
local u10 = math.pow(2, 48);
local u11 = math.pow(2, 40);
local u12 = math.pow(2, 32);
local u13 = math.pow(2, 24);
local u14 = math.pow(2, 16);
local u15 = math.pow(2, 8);
local p23 = false;
local p22 = read_string(8);
if p23 then
p22 = p22:reverse();
end;
local v15 = string.byte(string.sub(p22, 1, 1));
local v16 = string.byte(string.sub(p22, 2, 2));
local v17 = string.byte(string.sub(p22, 3, 3));
local v18 = string.byte(string.sub(p22, 4, 4));
local v19 = string.byte(string.sub(p22, 5, 5));
local v20 = string.byte(string.sub(p22, 6, 6));
local v21 = string.byte(string.sub(p22, 7, 7));
local v22 = string.byte(string.sub(p22, 8, 8));
local v23;
if v15 >= 128 then
v23 = 1;
else
v23 = 0;
end;
local v24 = v15 % 128 * 16 + math.floor(v16 / 16);
local v25 = v16 % 16 * u10 + v17 * u11 + v18 * u12 + v19 * u13 + v20 * u14 + v21 * u15 + v22;
if v24 == 2047 then
if v25 == 0 then
return math.pow(-1, v23) * math.huge;
end;
if v25 == u9 - 1 then
return (0 / 0);
end;
end;
if v24 == 0 then
return math.pow(-1, v23) * math.pow(2, v24 - 1023) * (v25 / u9);
end;
return math.pow(-1, v23) * math.pow(2, v24 - 1023) * (v25 / u9 + 1);
end
local function read_byte()
local current = bytecode:byte(stream_position);
stream_position = stream_position + 1;
return current;
end
local function read_lua_string()
local length = read1_function();
if (length == 0) then
return;
end
local result = string.sub(bytecode, stream_position, stream_position + length - 1);
stream_position = stream_position + length;
return result;
end
local function read_short()
local w, x = string.byte(bytecode, stream_position, stream_position + 1);
stream_position = stream_position + 2;
return (w * 256) + x;
--return read_sequence(2);
end
local function read_qword()
return read_dword() * 4294967296 + read_dword();
end
local readsize_dispatch_table = {
[1] = read_byte;
[2] = read_short;
[4] = read_dword;
[8] = read_qword;
}
local function read_chunk()
local code = { };
local constants = { };
local protos = { };
-- recovering debug info
local lineinfo = { };
local locvars = { };
local upvalue_names = { };
local chunk = {
lineinfo = lineinfo;
locvars = locvars;
upvalue_names = upvalue_names;
constants = constants;
code = code;
protos = protos;
};
-- init
chunk.name = string.sub(read_lua_string(), 1, -2);
chunk.line_defined = read1_function();
chunk.last_line_defined = read1_function();
chunk.nups = read_byte();
chunk.num_parameters = read_byte();
chunk.vararg_flag = read_byte();
chunk.max_stack_size = read_byte();
-- decode instructions
chunk.size_code = read1_function();
for i = 1, chunk.size_code do
local instruction = read_dword();
-- same insn format as vanilla lua with some changes
local opcode = instruction % 64;
local type = west_optype[opcode + 1];
-- unpack_Bits slow
local new_instruction = {
opcode = opcode,
type = type,
line = -1, -- TODO propagate
a = unpack_bits(instruction, 7, 14);
};
if (type == 'ABC') then
new_instruction.b = unpack_bits(instruction, 24, 32);
new_instruction.c = unpack_bits(instruction, 15, 23);
elseif (type == 'ABx') then
new_instruction.bx = unpack_bits(instruction, 15, 32);
elseif (type == 'AsBx') then
new_instruction.sbx = unpack_bits(instruction, 15, 32) - 131071;
end
-- emplace to instructions
code[i] = new_instruction;
end
-- decode constants
chunk.sizek = read1_function();
for i = 1, chunk.sizek do
local kst_type = read_byte();
local kst_result;
if (kst_type == 1) then -- LUA_TBOOL
kst_result = (read_byte() ~= 0);
elseif (kst_type == 0) then -- LUA_TNIL
kst_result = nil;
elseif (kst_type == 3) then -- LUA_TNUMBER
kst_result = read_double();
elseif (kst_type == 4) then -- LUA_TSTRING
kst_result = string.sub(read_lua_string(), 1, -2);
end
--print(kst_result)
constants[i] = kst_result;
end
-- decode protos
chunk.sizep = read1_function();
for i = 1, chunk.sizep do
protos[i] = read_chunk();
end
-- decode debug info (stripped atm)
do
chunk.size_lineinfo = read1_function();
print(chunk.size_lineinfo, chunk.size_code);
for i = 1, chunk.size_lineinfo do
chunk.code[i].line = read1_function(); -- propagate line
--lineinfo[i] = read1_function();
end
chunk.size_locvars = read1_function();
for i = 1, chunk.size_locvars do
locvars[i] = {
name = string.sub(read_lua_string(), 1, -2);
start_pc = read1_function();
end_pc = read1_function();
};
end
chunk.size_upvalues = read1_function();
for i = 1, chunk.size_upvalues do
upvalue_names[i] = string.sub(read_lua_string(), 1, -2);
end
end
return chunk;
end
-- read header
assert(read_string(4) == west_signature, '[west_transpiler]: west bytecode expected (header)');
assert(read_byte() == 0x51, 'expected lua 5.1');
assert(read_byte() == 1, 'invalid format'); -- format
read_byte(); -- endianess
local read1_size = read_byte();
local read2_size = read_byte();
read1_function = readsize_dispatch_table[read1_size];
read2_function = readsize_dispatch_table[read2_size];
assert(read_string(3) == '48 ');
return read_chunk();
end
end
-- instruction reference wrapper
local instruction_refwrapper = { };
do
instruction_refwrapper.__index = instruction_refwrapper;
function instruction_refwrapper.create_wrapper(value)
return setmetatable({
value_ = value; -- pointer to old instruction
next_ = nil;
prev_ = nil;
id_ = -1; -- for topological ordering
jmp_sucessor_ = nil;
sucessor_ = nil; -- alternative sucessor
}, instruction_refwrapper);
end
-- transforms instruction list into circular doubly linked list
function instruction_refwrapper.transform_wrappers(list)
local node = instruction_refwrapper.create_wrapper(list[1]);
local temp = node;
for i = 2, #list do
node = node:insert(list[i]);
end
return temp;
end
function instruction_refwrapper:transform_array()
local result = { };
do
local current = self;
while (current) do
local value = current.value_;
table.insert(result, value);
current = current.next_;
end
end
return result;
end
function instruction_refwrapper:pop_backward()
local result = self.prev_;
self.prev_.next_ = self.next_;
return result;
end
function instruction_refwrapper:pop_forward()
local result = self.next_;
self.prev_.next_ = self.next_;
return result;
end
function instruction_refwrapper:insert(value)
local node = instruction_refwrapper.create_wrapper(value);
if (self.next_) then
self.next_.prev_ = node;
node.next_ = self.next_;
end
node.prev_ = self;
self.next_ = node;
return node;
end
-- getters & setters
function instruction_refwrapper:get_value()
return self.value_;
end
end
-- set of conditional branch instructions (expect loadbool)
local conditional_set = {
[23] = true; -- EQ
[24] = true; -- LT
[25] = true; -- LE
[31] = true; -- FORLOOP
[33] = true; -- TFORLOOP
[26] = true; -- TEST
[27] = true; -- TESTSET
};
local function map_pcs(wrapper)
local pc_map = { };
do
local pc = 1;
local o_wrapper = wrapper;
while (o_wrapper) do
pc_map[pc] = o_wrapper;
o_wrapper = o_wrapper.next_;
pc = pc + 1;
end
end
return pc_map;
end
local function map_instructions(wrapper)
local pc_map = { };
do
local pc = 1;
local o_wrapper = wrapper;
while (o_wrapper) do
pc_map[o_wrapper] = pc;
o_wrapper = o_wrapper.next_;
pc = pc + 1;
end
end
return pc_map;
end
local function populate_sucessors(wrapper)
--local pc_map = { };
local pc_map = map_pcs(wrapper);
do
local pc = 1;
while (wrapper) do
local value = wrapper.value_;
if (value.opcode == 22) then -- JMP ONLY SUPPORTED RN
local target_wrapper = pc_map[value.sbx + pc + 1];
if (target_wrapper ~= nil) then
wrapper.jmp_sucessor_ = target_wrapper;
end
elseif (value.opcode == 32 or value.opcode == 31) then -- forprep & forloop
local target_wrapper = pc_map[value.sbx + pc + 1];
if (target_wrapper ~= nil) then
wrapper.sucessor_ = target_wrapper;
end
end
--[[elseif (conditional_set[value.opcode]) then
local target_wrapper = pc_map[pc + 1];
if (target_wrapper ~= nil) then
wrapper:set_sucessor(target_wrapper);
end
end]]
wrapper = wrapper.next_;
pc = pc + 1;
end
end
end
local instruction_toposort = { };
do
instruction_toposort.__index = instruction_toposort;
function instruction_toposort.create(begin)
return setmetatable({
begin_ = begin;
visited_ = { }; -- visited nodes
queue_ = { }; -- BFS queue
order_ = { }; -- post order of instructions
}, instruction_toposort);
end
function instruction_toposort:cleanup()
local wrapper = self.begin_;
while (wrapper) do
if (not self.visited_[wrapper]) then
wrapper = wrapper:pop_backward();
else
wrapper = wrapper.next_;
end
end
end
-- time complexity: O(v + e)
function instruction_toposort:run()
table.insert(self.queue_, self.begin_);
while (#self.queue_ > 0) do
local current = table.remove(self.queue_);
table.insert(self.order_, current);
self.visited_[current] = true;
while (current) do
local value = current.value_;
-- handler
local sucessor = current.jmp_sucessor_;
if (sucessor ~= nil) then
-- not conditional fallthough?
if (not conditional_set[current.prev_.value_.opcode]) then
local current_sucessor = sucessor.jmp_sucessor_;
-- jmp 1 mostly appears on conditional exprs (ex bin loadbool), skip out non conditional jmp 1's (coulda been optimized after sort aswell)
if (current.value_.sbx == 1 and not current_sucessor) then
table.insert(self.queue_, sucessor);
goto bfs_jumpback;
end
while (current_sucessor) do -- while (current and current.opcode ~= 22)
if (current_sucessor.value_.opcode ~= 22) then -- JMP
table.insert(self.queue_, current_sucessor);
goto bfs_jumpback;
end
current_sucessor = current_sucessor.jmp_sucessor_;
end
end
end
if (not self.visited_[current]) then
table.insert(self.order_, current);
self.visited_[current] = true;
end
--table.insert(queue, current);
current = current.next_;
end
::bfs_jumpback::
end
end
function instruction_toposort:get_order()
return self.order_;
end
end
-- todo opcode transformers
--[[
loadservice transformer (PSUEDOINSTRUCTION):
getglobal LOADSERVICE_A, KST('game')
self LOADSERVICE_A, LOADSERVICE_A, KST('GetService')
loadk LOADSERVICE_A + 2, LOADSERVICE_BX
call LOADSERVICE_A, 3, 2
for NOP just remove instruction
]]
-- chunk methods
local MAX_REG = 250;
local function allocate_registers(chunk, begin, offset, registers)
-- todo, and handle param
chunk.max_stack_size = chunk.max_stack_size + registers;
end
local function free_registers(chunk, begin, offset, registers)
-- todo
end
local function get_or_create_constant(chunk, kst)
for i = 1, chunk.sizek do
if (chunk.constants[i] == kst) then
return i - 1;
end
end
table.insert(chunk.constants, kst);
chunk.sizek = chunk.sizek + 1;
return chunk.sizek - 1;
end
local opcode_transformer = { };
do
opcode_transformer.__index = opcode_transformer;
function opcode_transformer.create(chunk, begin)
return setmetatable({
chunk_ = chunk;
begin_ = begin;
}, opcode_transformer);
end
function opcode_transformer:pass()
local wrapper = self.begin_;
while (wrapper) do
local value = wrapper.value_;
if (value.opcode == 38) then -- LOADSERVICE PSUEDOINSTRUCTION
-- required constants
local game_kst = get_or_create_constant(self.chunk_, 'game');
local getservice_kst = get_or_create_constant(self.chunk_, 'GetService');
local offset = value.a;
wrapper = wrapper:pop_backward();
local current_line = (wrapper.value_.line or -1) + 1;
wrapper = wrapper:insert{
opcode = 5;
type = 'ABx';
a = offset;
line = current_line;
bx = game_kst;
}:insert{
opcode = 11;
type = 'ABC';
a = offset;
b = offset;
line = current_line;
c = 256 + getservice_kst;
}:insert{
opcode = 1;
type = 'ABx';
a = offset + 2;
line = current_line;
bx = value.bx;
}:insert{
line = current_line;
opcode = 28;
type = 'ABC';
a = offset;
b = 3;
c = 2;
};
-- allocate registers
--allocate_registers(self.chunk_, self.begin_, offset, 2);
-- wrapper = result;
elseif (value.opcode == 63) then -- NOP
wrapper = wrapper:pop_backward();
else -- continue execution
wrapper = wrapper.next_;
end
end
end
end
local function relink_previous_wrappers(wrapper)
local o_prev = nil;
while (wrapper) do
wrapper.prev_ = o_prev;
o_prev = wrapper;
wrapper = wrapper.next_;
end
end
local function fix_target_branch_offsets(wrapper)
local instruction_pc_map = map_instructions(wrapper);
local current_pc = 1;
while (wrapper) do
local sucessor = wrapper.jmp_sucessor_ or wrapper.sucessor_;
if (sucessor) then
local pc = instruction_pc_map[sucessor];
if (pc) then
wrapper.value_.sbx = pc - current_pc - 1;
end
end
wrapper = wrapper.next_;
current_pc = current_pc + 1;
end
end
local varardic_opcodes = {
[28] = true;
[29] = true;
[30] = true;
[34] = true;
};
local function decode_chunk(chunk, wrapper)
local string_kst = get_or_create_constant(chunk, 'string');
local char_kst = get_or_create_constant(chunk, 'char') + 256;
local worklist = { };
--local decoded_set = { };
local function process_decode(start, current_wrapper)
local call_stack_track = 0;
local result = { };
while (current_wrapper) do
current_wrapper = current_wrapper.next_;
call_stack_track = call_stack_track + 1;
local current_value = current_wrapper.value_;
if (current_value.opcode ~= 1) then -- LOADK
break;
end
if (call_stack_track + start ~= current_value.a) then
break;
end
local constant_value = chunk.constants[current_value.bx + 1];
result[call_stack_track] = constant_value;
end
result = string.char(unpack(result));
return current_wrapper, result;
end
local o_wrapper = wrapper;
while (o_wrapper) do
local value = o_wrapper.value_;
local v = value;
if (v.type == 'ABC') then
-- print(west_openum[v.opcode + 1], v.a, v.b, v.c);
elseif (v.type == 'AsBx') then
--print(west_openum[v.opcode + 1],v.a, v.sbx);
elseif (v.type == 'ABx') then
--print(west_openum[v.opcode + 1],v.a, v.bx);
end
if (value.opcode == 5 and value.bx == string_kst) then -- getglobal
local next_wrapper = o_wrapper.next_;
local next_value = next_wrapper.value_;
if (next_value.opcode == 6 and next_value.a == value.a and next_value.b == value.a) then -- gettable A A
if (next_value.c > 255 and next_value.c == char_kst) then
-- process
local continue_wrapper, result = process_decode(next_value.a, next_wrapper);
local new_constant = get_or_create_constant(chunk, result);
local loadconstant_new = instruction_refwrapper.create_wrapper({
opcode = 1;
type = 'ABx';
line = value.line;
a = value.a;
bx = new_constant;
});
o_wrapper.value_ = loadconstant_new.value_;
o_wrapper.next_ = continue_wrapper.next_;
continue_wrapper.prev_ = o_wrapper;
table.insert(worklist, o_wrapper);
--decoded_set[o_wrapper] = true;
--wrapper.prev_.next_ = loadconstant_new;
--loadconstant_new.next_ = continue_wrapper.next_;
--loadconstant_new.prev_ = wrapper.prev_;
--wrapper = wrapper.next_;
end
end
end
o_wrapper = o_wrapper.next_;
end
-- fuck this shit im lazy
relink_previous_wrappers(wrapper);
-- we can assume that wild west replaces string constants to an extended string.char call (on intermediate level)
-- because B is going to be zero on TOP instructions where it has been called, hence we need
-- to fix B=0 return/setlist/tail/call instructions (reset L->top so can use only 1 return value)
while (#worklist > 0) do
local wrapper = table.remove(worklist, 1);
local old_reg = wrapper.value_.a;
wrapper = wrapper.next_;
while (wrapper) do
local value = wrapper.value_;
if (varardic_opcodes[value.opcode]) then
if (value.c == 0) then
break;
end
if (value.b == 0) then
if (old_reg < value.a) then
-- todo
break;
end
-- top is dynamic?
--local previous = wrapper.prev_;
--if (settop_opcodes[previous.value_.opcode]) then
-- if (previous.value_.c == 0) then
-- break;
-- end
--end
value.b = old_reg - value.a + 1; -- reset top to minimal parameter value
break;
end
elseif (wrapper == worklist[1]) then
break;
end
wrapper = wrapper.next_;
end
end
end
local function controlvar_add_optimization_pass(chunk, wrapper)
local kst_1 = get_or_create_constant(chunk, 1) + 256;
local num_additions = 0;
local add_wrapper = nil;
while (wrapper) do
local value = wrapper.value_;
if (value.opcode == 12 and value.a == value.b and value.c == kst_1) then
if (not add_wrapper) then
add_wrapper = wrapper;
end
num_additions = num_additions + 1;
elseif (add_wrapper) then
local kst = get_or_create_constant(chunk, num_additions);
add_wrapper.value_.c = 256 + kst;
add_wrapper.next_ = wrapper;
wrapper.prev_ = add_wrapper;
-- reset variables
add_wrapper = nil;
num_additions = 0;
end
wrapper = wrapper.next_;
end
end
local function transpile_chunk(chunk)
-- generate reference wrappers
local wrappers = instruction_refwrapper.transform_wrappers(chunk.code);
-- extract jmp sucessors
populate_sucessors(wrappers);
if deobfuscation_settings.enable_lower_optimizations then
-- fix string.char
decode_chunk(chunk, wrappers);
-- optimize control variables
controlvar_add_optimization_pass(chunk, wrappers);
end
-- perform topological order (min nodes, determined by unreachable PCs)
local sorter = instruction_toposort.create(wrappers);
sorter:run();
-- run cleanup
sorter:cleanup();
-- relink previous wrappers after topological sort
relink_previous_wrappers(wrappers);
-- transform custom opcode instructions
local transformer_pass = opcode_transformer.create(chunk, wrappers);
transformer_pass:pass();
-- fix branch offsets
fix_target_branch_offsets(wrappers);
-- transform wrappers -> array
chunk.code = instruction_refwrapper.transform_array(wrappers);
-- fix chunk values after optimizations
chunk.size_code = #chunk.code;
for i = 1, chunk.sizep do
transpile_chunk(chunk.protos[i]);
end
end
-- main
do
local input_file = io.open('test.bin', 'rb');
local input_bytecode = input_file:read('*a');
input_file:close();
local chunk = west_undump(input_bytecode);
transpile_chunk(chunk);
local result = vanilla_dumper.dump(chunk);
local output_file = io.open('output.luac', 'wb');
output_file:write(result);
output_file:close();
print('done');
end
ENJOY!
Warning: DO NOT DOWNLOAD anything from this page, you’re only here to copy the script!
[ad_2]