diff --git a/process.php b/process.php index 63800cd..50a23b6 100644 --- a/process.php +++ b/process.php @@ -69,7 +69,7 @@ class LuartexExtremeVM { return '{' . implode(',', $parts) . '}'; } } elseif (is_string($data)) { - return '"' . addslashes($data) . '"'; + return json_encode($data); } elseif (is_bool($data)) { return $data ? 'true' : 'false'; } elseif (is_null($data)) { @@ -157,6 +157,8 @@ class LuartexExtremeVM { $v_p = $this->genVar(); $v_r = $this->genVar(); $v_l = $this->genVar(); + $v_a = $this->genVar(); + $v_b = $this->genVar(); $k_str = implode(',', $this->keys); $consts_table = $this->toLuaTable($encryptedConsts); @@ -240,4 +242,4 @@ try { echo json_encode($result); } catch (Exception $e) { echo json_encode(['success' => false, 'error' => $e->getMessage()]); -} +} \ No newline at end of file diff --git a/standalone_test.php b/standalone_test.php new file mode 100644 index 0000000..acea440 --- /dev/null +++ b/standalone_test.php @@ -0,0 +1,240 @@ + $code]); + // Simulate process.php but with our mocks + // Instead of including, let's just copy the class. +"); + +// Actually, let's just make a standalone test that doesn't depend on db. +?> +rawCode = $code; + $this->keys = [rand(128, 255), rand(128, 255), rand(128, 255), rand(128, 255)]; + $this->setupOpcodes(); + } + + private function setupOpcodes() { + $ops = [ + 'LOADK', + 'GETGLOBAL', + 'SETGLOBAL', + 'CALL', + 'MOVE', + 'ADD', + 'SUB', + 'MUL', + 'DIV', + 'MOD', + 'POW', + 'JMP', + 'EQ', + 'LT', + 'LE', + 'RETURN', + 'GETTABLE', + 'SETTABLE', + 'NEWTABLE', + 'CLOSURE', + 'VARARG', + 'FORPREP', + 'FORLOOP' + ]; + shuffle($ops); + foreach ($ops as $index => $op) { + $this->opcodes[$op] = $index + 100; + } + } + + private function genVar() { + $chars = 'iI1l'; + $res = '_'; + for($i=0; $i<16; $i++) $res .= $chars[rand(0, 3)]; + return $res; + } + + private function toLuaTable($data) { + if (is_array($data)) { + $isList = true; + $expectedKey = 0; + foreach ($data as $k => $v) { + if ($k !== $expectedKey++) { + $isList = false; + break; + } + } + if ($isList) { + $parts = []; + foreach ($data as $v) { + $parts[] = $this->toLuaTable($v); + } + return '{' . implode(',', $parts) . '}'; + } else { + $parts = []; + foreach ($data as $k => $v) { + $key = is_string($k) ? "[" . addslashes($k) . "]" : "[$k]"; + $parts[] = $key . '=' . $this->toLuaTable($v); + } + return '{' . implode(',', $parts) . '}'; + } + } elseif (is_string($data)) { + return json_encode($data); + } elseif (is_bool($data)) { + return $data ? 'true' : 'false'; + } elseif (is_null($data)) { + return 'nil'; + } + return $data; + } + + private function addConst($val) { + $idx = array_search($val, $this->constants); + if ($idx === false) { + $this->constants[] = $val; + $idx = count($this->constants) - 1; + } + return $idx; + } + + private function encrypt($data) { + if (is_string($data)) { + $out = []; + for ($i = 0; $i < strlen($data); $i++) { + $out[] = ord($data[$i]) ^ $this->keys[$i % 4]; + } + return $out; + } + return (int)$data ^ $this->keys[0]; + } + + private function compile() { + $this->addConst("Luartex V3.5 Hardened VM"); + preg_match_all('/([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)?)\s*\((.*?)\)/', $this->rawCode, $matches); + if (isset($matches[1]) && !empty($matches[1])) { + foreach ($matches[1] as $idx => $funcName) { + $argString = isset($matches[2][$idx]) ? trim($matches[2][$idx]) : ''; + $fIdx = $this->addConst($funcName); + $this->instructions[] = [$this->opcodes['GETGLOBAL'], 0, $fIdx]; + if (empty($argString)) { + $this->instructions[] = [$this->opcodes['CALL'], 0, 0]; + } elseif (preg_match('/^["\\](.*)["\\]$/', $argString, $m)) { + $vIdx = $this->addConst($m[1]); + $this->instructions[] = [$this->opcodes['LOADK'], 1, $vIdx]; + $this->instructions[] = [$this->opcodes['CALL'], 0, 1]; + } elseif (is_numeric($argString)) { + $vIdx = $this->addConst((float)$argString); + $this->instructions[] = [$this->opcodes['LOADK'], 1, $vIdx]; + $this->instructions[] = [$this->opcodes['CALL'], 0, 1]; + } else { + $this->instructions[] = [$this->opcodes['CALL'], 0, 0]; + } + } + } else { + $this->addConst("Luartex Protection Active"); + } + for ($i=0; $i<10; $i++) { + $this->instructions[] = [$this->opcodes['MOVE'], rand(0, 50), rand(0, 50)]; + } + $this->instructions[] = [$this->opcodes['RETURN'], 0, 0]; + } + + public function build() { + $this->compile(); + $encryptedConsts = []; + foreach ($this->constants as $c) { + $encryptedConsts[] = $this->encrypt($c); + } + $encryptedInsts = []; + foreach ($this->instructions as $inst) { + $encryptedInsts[] = [ + (int)$inst[0] ^ $this->keys[0], + (int)$inst[1] ^ $this->keys[1], + (int)$inst[2] ^ $this->keys[2] + ]; + } + + $v_v = $this->genVar(); + $v_k = $this->genVar(); + $v_s = $this->genVar(); + $v_i = $this->genVar(); + $v_c = $this->genVar(); + $v_o = $this->genVar(); + $v_d = $this->genVar(); + $v_g = $this->genVar(); + $v_x = $this->genVar(); + $v_e = $this->genVar(); + $v_p = $this->genVar(); + $v_r = $this->genVar(); + $v_l = $this->genVar(); + $v_a = $this->genVar(); + $v_b = $this->genVar(); + + $k_str = implode(',',$this->keys); + $consts_table = $this->toLuaTable($encryptedConsts); + $insts_table = $this->toLuaTable($encryptedInsts); + $opcodes_table = $this->toLuaTable($this->opcodes); + + $lua = "-- ts was obfuscated by Luartex V3.2\n"; + $lua .= "local " . $v_v . " = {C = " . $consts_table . ", I = " . $insts_table . ", O = " . $opcodes_table . "}; "; + $lua .= "local " . $v_k . " = {" . $k_str . "}; "; + $lua .= "return (function(...) "; + $lua .= "local " . $v_e . " = getfenv and getfenv() or _G; "; + $lua .= "local " . $v_p . " = tick(); "; + $lua .= "local function " . $v_g . "(_f) "; + $lua .= "if debug and debug.info then "; + $lua .= "local _s, _l = debug.info(_f or print, 'sl'); if _s ~= '[C]' then while true do " . $v_p . " = " . $v_p . " + 1 end end "; + $lua .= "end "; + $lua .= "if tick() - " . $v_p . " > 10 then while true do end end "; + $lua .= "end; "; + $lua .= "local function " . $v_x . "(_t) "; + $lua .= "if type(_t) == 'table' then "; + $lua .= "local _r = ''; for _j = 1, #_t do "; + $lua .= "local _idx = ((_j - 1) % 4) + 1; "; + $lua .= "_r = _r .. string.char(bit32.bxor(_t[_j], " . $v_k . "[_idx])) "; + $lua .= "end return _r "; + $lua .= "end return bit32.bxor(_t, " . $v_k . "[1]) "; + $lua .= "end; "; + $lua .= "local " . $v_r . " = {}; "; + $lua .= "local " . $v_l . " = 1; "; + $lua .= "while " . $v_l . " <= #" . $v_v . ".I do "; + $lua .= "local " . $v_i . " = " . $v_v . ".I[" . $v_l . "]; "; + $lua .= "local " . $v_o . " = bit32.bxor(" . $v_i . "[1], " . $v_k . "[1]); "; + $lua .= "local " . $v_a . " = bit32.bxor(" . $v_i . "[2], " . $v_k . "[2]); "; + $lua .= "local " . $v_b . " = bit32.bxor(" . $v_i . "[3], " . $v_k . "[3]); "; + + $lua .= "if " . $v_o . " == " . $this->opcodes['GETGLOBAL'] . " then "; + $lua .= "local _gn = " . $v_x . "(" . $v_v . ".C[" . $v_b . "+1]); "; + $lua .= "local _gv = " . $v_e . "; for _p in _gn:gmatch('%.') do _gv = _gv[_p] end; "; + $lua .= $v_r . "[" . $v_a . "] = _gv; "; + $lua .= "elseif " . $v_o . " == " . $this->opcodes['LOADK'] . " then "; + $lua .= $v_r . "[" . $v_a . "] = " . $v_x . "(" . $v_v . ".C[" . $v_b . "+1]); "; + $lua .= "elseif " . $v_o . " == " . $this->opcodes['CALL'] . " then "; + $lua .= $v_g . "(" . $v_r . "[" . $v_a . "]); "; + $lua .= "if " . $v_b . " == 0 then " . $v_r . "[" . $v_a . "]() else " . $v_r . "[" . $v_a . "](" . $v_r . "[" . $v_a . "+1]) end; "; + $lua .= "elseif " . $v_o . " == " . $this->opcodes['RETURN'] . " then "; + $lua .= "return; "; + $lua .= "end; "; + + $lua .= $v_l . " = " . $v_l . " + 1; "; + $lua .= "end; "; + $lua .= "end)(...)"; + return $lua; + } +} +$vm = new LuartexExtremeVM('print("test")'); +echo $vm->build(); diff --git a/test_obf.php b/test_obf.php new file mode 100644 index 0000000..c0414a1 --- /dev/null +++ b/test_obf.php @@ -0,0 +1,24 @@ +