366 lines
15 KiB
PHP
366 lines
15 KiB
PHP
<?php
|
|
header('Content-Type: application/json');
|
|
require_once __DIR__ . '/db/config.php';
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
if (!$input) {
|
|
$input = json_decode(file_get_contents('php://stdin'), true);
|
|
}
|
|
|
|
if (!$input || empty($input['code'])) {
|
|
echo json_encode(['success' => false, 'error' => 'No code provided']);
|
|
exit;
|
|
}
|
|
|
|
$code = $input['code'];
|
|
|
|
class LuartexHyperionV5 {
|
|
private $rawCode;
|
|
private $constants = [];
|
|
private $instructions = [];
|
|
private $keys = [];
|
|
private $opMap = [];
|
|
private $vm_id;
|
|
|
|
public function __construct($code) {
|
|
$this->rawCode = $code;
|
|
$this->vm_id = bin2hex(random_bytes(4));
|
|
for ($i = 0; $i < 32; $i++) {
|
|
$this->keys[] = rand(0, 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', 'AND', 'OR', 'NOT', 'LEN',
|
|
'TFORLOOP', 'SETLIST', 'CLOSE', 'JUNK1', 'JUNK2', 'JUNK3', 'OPAQUE',
|
|
'SET_GLOBAL_CONST', 'MOVE_GLOBAL', 'CALL_GLOBAL_K', 'CALL_GLOBAL_V'
|
|
];
|
|
shuffle($ops);
|
|
foreach ($ops as $op) {
|
|
$this->opMap[$op] = rand(1000, 99999);
|
|
}
|
|
}
|
|
|
|
private function genVar($len = 16) {
|
|
$chars = 'l1Ii_';
|
|
$res = 'L_';
|
|
for($i=0; $i<$len; $i++) $res .= $chars[rand(0, 4)];
|
|
return $res;
|
|
}
|
|
|
|
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 genOpaquePredicate($type = 'true') {
|
|
$truePreds = [
|
|
"(math.pi > 3)",
|
|
"(type(math.abs) == 'function')",
|
|
"(math.floor(10.5) == 10)",
|
|
"(#('Hyperion') == 8)",
|
|
"(math.sin(0) == 0)",
|
|
"(not (1 == 0))",
|
|
"(math.abs(-1) == 1)",
|
|
"(math.ceil(5.1) == 6)"
|
|
];
|
|
$falsePreds = [
|
|
"(math.pi < 3)",
|
|
"(type(math.abs) == 'string')",
|
|
"(math.floor(10.5) == 11)",
|
|
"(#('Hyperion') == 9)",
|
|
"(math.sin(0) == 1)",
|
|
"(1 == 0)",
|
|
"(math.abs(-1) == 0)",
|
|
"(math.ceil(5.1) == 5)"
|
|
];
|
|
|
|
if ($type === 'true') {
|
|
return $truePreds[array_rand($truePreds)];
|
|
} else {
|
|
return $falsePreds[array_rand($falsePreds)];
|
|
}
|
|
}
|
|
|
|
private function foldInstructions() {
|
|
$folded = [];
|
|
$i = 0;
|
|
$count = count($this->instructions);
|
|
|
|
while ($i < $count) {
|
|
$inst = $this->instructions[$i];
|
|
|
|
if ($i + 2 < $count) {
|
|
$n1 = $this->instructions[$i + 1];
|
|
$n2 = $this->instructions[$i + 2];
|
|
|
|
// Pattern: GETGLOBAL(0, f) + LOADK(1, v) + CALL(0, 1) -> CALL_GLOBAL_K(f, v)
|
|
if ($inst[0] === $this->opMap['GETGLOBAL'] && $inst[1] === 0 &&
|
|
$n1[0] === $this->opMap['LOADK'] && $n1[1] === 1 &&
|
|
$n2[0] === $this->opMap['CALL'] && $n2[1] === 0 && $n2[2] === 1) {
|
|
$folded[] = [$this->opMap['CALL_GLOBAL_K'], $inst[2], $n1[2]];
|
|
$i += 3;
|
|
continue;
|
|
}
|
|
|
|
// Pattern: GETGLOBAL(0, f) + GETGLOBAL(1, v) + CALL(0, 1) -> CALL_GLOBAL_V(f, v)
|
|
if ($inst[0] === $this->opMap['GETGLOBAL'] && $inst[1] === 0 &&
|
|
$n1[0] === $this->opMap['GETGLOBAL'] && $n1[1] === 1 &&
|
|
$n2[0] === $this->opMap['CALL'] && $n2[1] === 0 && $n2[2] === 1) {
|
|
$folded[] = [$this->opMap['CALL_GLOBAL_V'], $inst[2], $n1[2]];
|
|
$i += 3;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($i + 1 < $count) {
|
|
$next = $this->instructions[$i + 1];
|
|
|
|
// Pattern: LOADK(0, v) + SETGLOBAL(0, s) -> SET_GLOBAL_CONST(s, v)
|
|
if ($inst[0] === $this->opMap['LOADK'] && $next[0] === $this->opMap['SETGLOBAL'] && $inst[1] === 0 && $next[1] === 0) {
|
|
$folded[] = [$this->opMap['SET_GLOBAL_CONST'], $next[2], $inst[2]];
|
|
$i += 2;
|
|
continue;
|
|
}
|
|
|
|
// Pattern: GETGLOBAL(0, v) + SETGLOBAL(0, s) -> MOVE_GLOBAL(s, v)
|
|
if ($inst[0] === $this->opMap['GETGLOBAL'] && $next[0] === $this->opMap['SETGLOBAL'] && $inst[1] === 0 && $next[1] === 0) {
|
|
$folded[] = [$this->opMap['MOVE_GLOBAL'], $next[2], $inst[2]];
|
|
$i += 2;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$folded[] = $inst;
|
|
$i++;
|
|
}
|
|
$this->instructions = $folded;
|
|
}
|
|
|
|
private function compile() {
|
|
$this->addConst("Hyperion V5.8 - Instruction Folding");
|
|
|
|
$cleanCode = $this->rawCode;
|
|
// Simple comment stripping
|
|
$cleanCode = preg_replace('/--\[\[.*?\].*?\]\]--/s', '', $cleanCode);
|
|
$cleanCode = preg_replace('/--.*$/m', '', $cleanCode);
|
|
|
|
$tokens = preg_split('/[;
|
|
]+/', $cleanCode);
|
|
|
|
foreach ($tokens as $token) {
|
|
$token = trim($token);
|
|
if (empty($token)) continue;
|
|
|
|
if (rand(1, 10) > 6) {
|
|
if (rand(0, 1) == 0) {
|
|
$this->instructions[] = [$this->opMap['JUNK1'], rand(0, 255), rand(0, 255)];
|
|
} else {
|
|
$this->instructions[] = [$this->opMap['OPAQUE'], rand(0, 255), rand(0, 255)];
|
|
}
|
|
}
|
|
|
|
if (preg_match('/^([a-zA-Z_]\w*(?:[.:]\w*)*)\s*\((.*?)\)$/', $token, $m)) {
|
|
$this->emitCall($m[1], $m[2]);
|
|
}
|
|
elseif (preg_match('/^(?:local\s+)?([a-zA-Z_]\w*)\s*=\s*(.*)$/', $token, $m)) {
|
|
$this->emitAssignment($m[1], $m[2]);
|
|
}
|
|
}
|
|
|
|
$this->foldInstructions();
|
|
$this->instructions[] = [$this->opMap['RETURN'], 0, 0];
|
|
}
|
|
|
|
private function emitCall($funcName, $argStr) {
|
|
$fIdx = $this->addConst($funcName);
|
|
$this->instructions[] = [$this->opMap['GETGLOBAL'], 0, $fIdx];
|
|
|
|
$args = [];
|
|
if (!empty(trim($argStr))) {
|
|
$rawArgs = explode(',', $argStr);
|
|
foreach ($rawArgs as $idx => $arg) {
|
|
$arg = trim($arg);
|
|
$rIdx = $idx + 1;
|
|
if (preg_match('/^["\'](.*)["\']$/s', $arg, $m)) {
|
|
$vIdx = $this->addConst($m[1]);
|
|
$this->instructions[] = [$this->opMap['LOADK'], $rIdx, $vIdx];
|
|
} elseif (is_numeric($arg)) {
|
|
$vIdx = $this->addConst((float)$arg);
|
|
$this->instructions[] = [$this->opMap['LOADK'], $rIdx, $vIdx];
|
|
} else {
|
|
$vIdx = $this->addConst($arg);
|
|
$this->instructions[] = [$this->opMap['GETGLOBAL'], $rIdx, $vIdx];
|
|
}
|
|
$args[] = $rIdx;
|
|
}
|
|
}
|
|
$this->instructions[] = [$this->opMap['CALL'], 0, count($args)];
|
|
}
|
|
|
|
private function emitAssignment($var, $val) {
|
|
$val = trim($val);
|
|
if (preg_match('/^["\'](.*)["\']$/s', $val, $m)) {
|
|
$vIdx = $this->addConst($m[1]);
|
|
$this->instructions[] = [$this->opMap['LOADK'], 0, $vIdx];
|
|
} elseif (is_numeric($val)) {
|
|
$vIdx = $this->addConst((float)$val);
|
|
$this->instructions[] = [$this->opMap['LOADK'], 0, $vIdx];
|
|
} else {
|
|
$vIdx = $this->addConst($val);
|
|
$this->instructions[] = [$this->opMap['GETGLOBAL'], 0, $vIdx];
|
|
}
|
|
|
|
$sIdx = $this->addConst($var);
|
|
$this->instructions[] = [$this->opMap['SETGLOBAL'], 0, $sIdx];
|
|
}
|
|
|
|
private function serializeAll() {
|
|
$bin = "";
|
|
|
|
// 1. Instructions
|
|
$bin .= pack("N", count($this->instructions));
|
|
foreach ($this->instructions as $inst) {
|
|
$op = (int)$inst[0];
|
|
$bin .= chr(($op >> 16) & 0xFF) . chr(($op >> 8) & 0xFF) . chr($op & 0xFF);
|
|
$bin .= pack("n", (int)$inst[1]);
|
|
$bin .= pack("n", (int)$inst[2]);
|
|
}
|
|
|
|
// 2. Constants
|
|
$bin .= pack("N", count($this->constants));
|
|
foreach ($this->constants as $c) {
|
|
if (is_string($c)) {
|
|
$bin .= chr(1);
|
|
$bin .= pack("N", strlen($c));
|
|
$bin .= $c;
|
|
} elseif (is_numeric($c)) {
|
|
$bin .= chr(2);
|
|
$sNum = (string)$c;
|
|
$bin .= pack("N", strlen($sNum));
|
|
$bin .= $sNum;
|
|
} else {
|
|
$bin .= chr(0);
|
|
}
|
|
}
|
|
|
|
$keyLen = count($this->keys);
|
|
$enc = "";
|
|
for ($i = 0; $i < strlen($bin); $i++) {
|
|
$enc .= chr(ord($bin[$i]) ^ $this->keys[$i % $keyLen] ^ (($i * 13) % 256));
|
|
}
|
|
|
|
return bin2hex($enc);
|
|
}
|
|
|
|
public function build() {
|
|
$this->compile();
|
|
|
|
$k_v = $this->genVar(12); $b_v = $this->genVar(12); $e_v = $this->genVar(12);
|
|
$f_v = $this->genVar(12); $d_v = $this->genVar(12); $c_v = $this->genVar(12);
|
|
|
|
$k_str = implode(',', $this->keys);
|
|
$allHex = $this->serializeAll();
|
|
|
|
$lua = "-- [[ Hyperion Engine V5.8 - Instruction Folding ]] --\n";
|
|
$lua .= "local " . $k_v . " = { " . $k_str . " }; ";
|
|
$lua .= "local " . $b_v . " = \"" . $allHex . "\"; ";
|
|
$lua .= "local " . $e_v . " = (getgenv and getgenv()) or (getfenv and getfenv(0)) or _G; ";
|
|
|
|
$lua .= "local function _H(h) local b = {}; for i = 1, #h, 2 do b[#b+1] = tonumber(h:sub(i, i+1), 16) end return b end; ";
|
|
$lua .= "local function _D(b) local o = {}; for i = 1, #b do local k = ((i - 1) % 32) + 1; o[i] = bit32.bxor(b[i], " . $k_v . "[k], ((i - 1) * 13) % 256) end return o end; ";
|
|
$lua .= "local " . $d_v . " = _D(_H(" . $b_v . ")); ";
|
|
|
|
$lua .= "local function _R32(b, p) return b[p]*16777216 + b[p+1]*65536 + b[p+2]*256 + b[p+3] end; ";
|
|
$lua .= "local function _R16(b, p) return b[p]*256 + b[p+1] end; ";
|
|
|
|
$lua .= "local ic = _R32(" . $d_v . ", 1); ";
|
|
$lua .= "local co = 5 + ic * 7; ";
|
|
$lua .= "local cc = _R32(" . $d_v . ", co); ";
|
|
|
|
$lua .= "local " . $c_v . " = {}; ";
|
|
$lua .= "local cu = co + 4; ";
|
|
$lua .= "for i = 1, cc do ";
|
|
$lua .= "local t = " . $d_v . "[cu]; cu = cu + 1; ";
|
|
$lua .= "if t == 1 or t == 2 then ";
|
|
$lua .= "local l = _R32(" . $d_v . ", cu); cu = cu + 4; ";
|
|
$lua .= "local s = {}; for j = 1, l do s[j] = string.char(" . $d_v . "[cu]); cu = cu + 1 end; ";
|
|
$lua .= "s = table.concat(s); if t == 2 then " . $c_v . "[i] = tonumber(s) else " . $c_v . "[i] = s end; ";
|
|
$lua .= "else cu = cu + 1 end end; ";
|
|
|
|
$lua .= "local function " . $f_v . "(p) local o = 5 + (p - 1) * 7; ";
|
|
$lua .= "return " . $d_v . "[o]*65536 + " . $d_v . "[o+1]*256 + " . $d_v . "[o+2], _R16(" . $d_v . ", o+3), _R16(" . $d_v . ", o+5) end; ";
|
|
|
|
$lua .= "local function _V() local stack = {}; local pc = 1; while true do local op, a, b = " . $f_v . "(pc); ";
|
|
|
|
$cases = [];
|
|
|
|
$op_getglobal = $this->opMap['GETGLOBAL'];
|
|
$cases[] = "elseif op == $op_getglobal then if " . $this->genOpaquePredicate('true') . " then local n = " . $c_v . "[b + 1]; local t = " . $e_v . "; for p in n:gmatch('[^.:]+') do t = t[p] end; stack[a] = t; else pc = pc + 666 end; ";
|
|
|
|
$op_loadk = $this->opMap['LOADK'];
|
|
$cases[] = "elseif op == $op_loadk then if " . $this->genOpaquePredicate('false') . " then pc = pc * 2 else stack[a] = " . $c_v . "[b + 1] end; ";
|
|
|
|
$op_call = $this->opMap['CALL'];
|
|
$cases[] = "elseif op == $op_call then if " . $this->genOpaquePredicate('true') . " then local f = stack[a]; local args = {}; for m = 1, b do args[m] = stack[a + m] end; local ok, err = pcall(f, unpack(args)); if not ok then error('VM Error: ' .. tostring(err)) end; end; ";
|
|
|
|
$op_setglobal = $this->opMap['SETGLOBAL'];
|
|
$cases[] = "elseif op == $op_setglobal then if " . $this->genOpaquePredicate('true') . " then local n = " . $c_v . "[b + 1]; " . $e_v . "[n] = stack[a]; end; ";
|
|
|
|
$op_move = $this->opMap['MOVE'];
|
|
$cases[] = "elseif op == $op_move then stack[a] = stack[b]; ";
|
|
|
|
$op_return = $this->opMap['RETURN'];
|
|
$cases[] = "elseif op == $op_return then if " . $this->genOpaquePredicate('false') . " then print('DEAD CODE') else return end; ";
|
|
|
|
$op_junk1 = $this->opMap['JUNK1'];
|
|
$cases[] = "elseif op == $op_junk1 then if " . $this->genOpaquePredicate('false') . " then pc = -1 end; ";
|
|
|
|
$op_opaque = $this->opMap['OPAQUE'];
|
|
$cases[] = "elseif op == $op_opaque then if " . $this->genOpaquePredicate('true') . " then local x = math.sqrt(a * b); stack[0] = x; else stack[0] = 0; end; ";
|
|
|
|
// Super-Opcodes
|
|
$op_set_global_const = $this->opMap['SET_GLOBAL_CONST'];
|
|
$cases[] = "elseif op == $op_set_global_const then if " . $this->genOpaquePredicate('true') . " then local n = " . $c_v . "[a + 1]; " . $e_v . "[n] = " . $c_v . "[b + 1]; end; ";
|
|
|
|
$op_move_global = $this->opMap['MOVE_GLOBAL'];
|
|
$cases[] = "elseif op == $op_move_global then if " . $this->genOpaquePredicate('true') . " then local sn = " . $c_v . "[a + 1]; local vn = " . $c_v . "[b + 1]; local t = " . $e_v . "; for p in vn:gmatch('[^.:]+') do t = t[p] end; " . $e_v . "[sn] = t; end; ";
|
|
|
|
$op_call_global_k = $this->opMap['CALL_GLOBAL_K'];
|
|
$cases[] = "elseif op == $op_call_global_k then if " . $this->genOpaquePredicate('true') . " then local fn = " . $c_v . "[a + 1]; local t = " . $e_v . "; for p in fn:gmatch('[^.:]+') do t = t[p] end; t(" . $c_v . "[b + 1]); end; ";
|
|
|
|
$op_call_global_v = $this->opMap['CALL_GLOBAL_V'];
|
|
$cases[] = "elseif op == $op_call_global_v then if " . $this->genOpaquePredicate('true') . " then local fn = " . $c_v . "[a + 1]; local vn = " . $c_v . "[b + 1]; local f = " . $e_v . "; for p in fn:gmatch('[^.:]+') do f = f[p] end; local v = " . $e_v . "; for p in vn:gmatch('[^.:]+') do v = v[p] end; f(v); end; ";
|
|
|
|
shuffle($cases);
|
|
$lua .= "if false then " . implode(" ", $cases) . " end; pc = pc + 1; end end; ";
|
|
$lua .= "pcall(_V); ";
|
|
|
|
return [
|
|
'success' => true,
|
|
'protected_code' => $lua,
|
|
'stats' => [
|
|
'original_size' => strlen($this->rawCode),
|
|
'protected_size' => strlen($lua),
|
|
'vm_version' => '5.8-instruction-folding'
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
try {
|
|
$vm = new LuartexHyperionV5($code);
|
|
echo json_encode($vm->build());
|
|
} catch (Exception $e) {
|
|
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
|
}
|