Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,7 +1,3 @@
|
||||
from django.contrib import admin
|
||||
from .models import ScriptLog
|
||||
|
||||
@admin.register(ScriptLog)
|
||||
class ScriptLogAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'input_size', 'output_size', 'created_at')
|
||||
readonly_fields = ('created_at',)
|
||||
# Register your models here.
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
# Generated by Django 5.2.7 on 2026-01-31 12:29
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ScriptLog',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('input_code', models.TextField()),
|
||||
('output_code', models.TextField()),
|
||||
('input_size', models.IntegerField()),
|
||||
('output_size', models.IntegerField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -1,11 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
class ScriptLog(models.Model):
|
||||
input_code = models.TextField()
|
||||
output_code = models.TextField()
|
||||
input_size = models.IntegerField()
|
||||
output_size = models.IntegerField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"ScriptLog {self.id} - {self.created_at}"
|
||||
# Create your models here.
|
||||
|
||||
@ -1,263 +0,0 @@
|
||||
import random
|
||||
import string
|
||||
import base64
|
||||
import json
|
||||
import hashlib
|
||||
import re
|
||||
from .parser import Lexer, Parser
|
||||
|
||||
class LuauVMObfuscator:
|
||||
def __init__(self):
|
||||
self.opcodes = [
|
||||
"MOVE", "LOADK", "LOADBOOL", "LOADNIL", "GETGLOBAL", "SETGLOBAL",
|
||||
"GETTABLE", "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"
|
||||
]
|
||||
|
||||
self.k1, self.k2, self.k3 = random.randint(500, 2000), random.randint(500, 2000), random.randint(500, 2000)
|
||||
self.op_to_id = {name: ((self.opcodes.index(name) + self.k1) ^ self.k2) + self.k3 for name in self.opcodes}
|
||||
self.var_map, self.used_vars = {}, set()
|
||||
|
||||
def get_var(self, hint="var"):
|
||||
if hint in self.var_map: return self.var_map[hint]
|
||||
c = "Il1"; l = random.randint(24, 32)
|
||||
v = "_" + "".join(random.choice(c) for _ in range(l))
|
||||
while v in self.used_vars: v = "_" + "".join(random.choice(c) for _ in range(l))
|
||||
self.used_vars.add(v); self.var_map[hint] = v
|
||||
return v
|
||||
|
||||
def to_expr(self, n):
|
||||
r = random.randint(1, 1000)
|
||||
ch = random.choice(['add', 'sub', 'xor'])
|
||||
if ch == 'add': return f"({n-r}+{r})"
|
||||
if ch == 'sub': return f"({n+r}-{r})"
|
||||
return f"bit32.bxor({n^r},{r})"
|
||||
|
||||
def encrypt_string(self, s, key):
|
||||
res, last = [], key % 256
|
||||
for i, c in enumerate(s):
|
||||
k = (key + i + last) % 256
|
||||
last = ord(c)
|
||||
res.append(chr(ord(c) ^ k))
|
||||
return "".join(res)
|
||||
|
||||
def minify(self, code):
|
||||
code = re.sub(r'--.*', '', code)
|
||||
code = re.sub(r'\s+', ' ', code)
|
||||
for op in ['==', '~=', '<=', '>=', '=', r'\+', r'\*', '/', '>', '<', ',', ';', ':', '-']:
|
||||
code = re.sub(r'\s*' + op + r'\s*', op.replace('\\', ''), code)
|
||||
return code.strip()
|
||||
|
||||
def generate_vm_source(self, bytecode):
|
||||
raw_ins = bytecode['instructions']
|
||||
indices = list(range(len(raw_ins)))
|
||||
random.shuffle(indices); pos_map = {orig: i for i, orig in enumerate(indices)}
|
||||
final_insts = []
|
||||
for orig_idx in indices:
|
||||
inst = raw_ins[orig_idx]
|
||||
next_sh = pos_map[orig_idx + 1] if orig_idx + 1 < len(raw_ins) else 0
|
||||
final_insts.append([inst[0]&0xFF,(inst[0]>>8)&0xFF,inst[1]&0xFF,inst[2]&0xFF,inst[3]&0xFF,next_sh&0xFF,(next_sh>>8)&0xFF])
|
||||
inst_str = "".join(chr(i) for inst in final_insts for i in inst)
|
||||
inst_b64 = base64.b64encode(inst_str.encode('latin-1')).decode()
|
||||
|
||||
consts = []
|
||||
salt = random.randint(100000, 999999)
|
||||
for i, c in enumerate(bytecode['constants']):
|
||||
if c['type'] == 'string':
|
||||
k = (i * 149 + salt) % 256; enc = self.encrypt_string(c['value'], k)
|
||||
consts.append({"t": 1, "v": base64.b64encode(enc.encode('latin-1')).decode()})
|
||||
elif c['type'] == 'number': consts.append({"t": 2, "v": c['value']})
|
||||
else: consts.append({"t": 3, "v": str(c['value'])})
|
||||
|
||||
v = {
|
||||
"BIT": "bit32", "ENV": self.get_var("env"), "DEC": self.get_var("dec"),
|
||||
"INST": self.get_var("inst"), "CONSTS": self.get_var("consts"), "SALT": self.to_expr(salt),
|
||||
"EXEC": self.get_var("exec"), "REGS": self.get_var("regs"), "CURR": self.get_var("curr"),
|
||||
"PTR": self.get_var("ptr"), "OP": self.get_var("op"), "A": self.get_var("a"),
|
||||
"B": self.get_var("b"), "C": self.get_var("c"), "UNP": "unpack or table.unpack",
|
||||
"SPW": "task and task.spawn or spawn", "JSON": "game:GetService('HttpService')",
|
||||
"CHARS": self.get_var("chars"), "LOOKUP": self.get_var("lookup"), "GETC": self.get_var("getc")
|
||||
}
|
||||
|
||||
vm_lua = f'''local {v['ENV']}=setmetatable({{}},{{__index=getfenv()}});
|
||||
local {v['CHARS']}='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
local {v['LOOKUP']}={{}};
|
||||
for i=1,64 do {v['LOOKUP']}[{v['CHARS']}:sub(i,i)]=i-1 end;
|
||||
local function {v['DEC']}(data)
|
||||
data=data:gsub('[^%a%d%+/]','');
|
||||
local res={{}};
|
||||
for i=1,#data,4 do
|
||||
local a,b,c,d={v['LOOKUP']}[data:sub(i,i)],{v['LOOKUP']}[data:sub(i+1,i+1)],{v['LOOKUP']}[data:sub(i+2,i+2)],{v['LOOKUP']}[data:sub(i+3,i+3)];
|
||||
local n={v['BIT']}.lshift(a or 0,18)+{v['BIT']}.lshift(b or 0,12)+{v['BIT']}.lshift(c or 0,6)+(d or 0);
|
||||
res[#res+1]=string.char({v['BIT']}.extract(n,16,8));
|
||||
if c then res[#res+1]=string.char({v['BIT']}.extract(n,8,8)) end;
|
||||
if d then res[#res+1]=string.char({v['BIT']}.extract(n,0,8)) end;
|
||||
end;
|
||||
return table.concat(res);
|
||||
end;
|
||||
local {v['INST']}={v['DEC']}('{inst_b64}');
|
||||
local {v['CONSTS']}={v['JSON']}:JSONDecode([=[{json.dumps(consts)}]=]);
|
||||
local {v['EXEC']}=function()
|
||||
local {v['REGS']}={{}};
|
||||
local {v['CURR']}={self.to_expr(pos_map[0])};
|
||||
local function {v['GETC']}(idx)
|
||||
local c={v['CONSTS']}[idx+1];
|
||||
if not c then return end;
|
||||
if c.t==1 then
|
||||
local r,k={v['DEC']}(c.v),(idx*149+{v['SALT']})%256;
|
||||
local res,lst={{}},k%256;
|
||||
for i=1,#r do
|
||||
local char={v['BIT']}.bxor(string.byte(r,i),(k+i+lst-1)%256);
|
||||
res[i]=string.char(char);
|
||||
lst=char;
|
||||
end;
|
||||
return table.concat(res);
|
||||
elseif c.t==3 then return c.v=='true' and true or (c.v=='false' and false or nil) end;
|
||||
return c.v;
|
||||
end;
|
||||
while true do
|
||||
local {v['PTR']}={v['CURR']}*7+1;
|
||||
local b1,b2,b3,b4,b5,b6,b7=string.byte({v['INST']},{v['PTR']},{v['PTR']}+6);
|
||||
if not b1 then break end;
|
||||
{v['CURR']}=b6+({v['BIT']}.lshift(b7,8));
|
||||
local {v['OP']}={v['BIT']}.bxor(b1+{v['BIT']}.lshift(b2,8),{self.to_expr(self.k2)})-{self.to_expr(self.k1)};
|
||||
local {v['A']},{v['B']},{v['C']}=b3,b4,b5;
|
||||
if {v['OP']}=={self.opcodes.index('MOVE')} then {v['REGS']}[{v['A']}]={v['REGS']}[{v['B']}]
|
||||
elseif {v['OP']}=={self.opcodes.index('LOADK')} then {v['REGS']}[{v['A']}]={v['GETC']}({v['B']})
|
||||
elseif {v['OP']}=={self.opcodes.index('GETGLOBAL')} then {v['REGS']}[{v['A']}]={v['ENV']}[{v['GETC']}({v['B']})]
|
||||
elseif {v['OP']}=={self.opcodes.index('SETGLOBAL')} then {v['ENV']}[{v['GETC']}({v['B']})]={v['REGS']}[{v['A']}]
|
||||
elseif {v['OP']}=={self.opcodes.index('GETTABLE')} then
|
||||
local b={v['REGS']}[{v['B']}];
|
||||
if b then {v['REGS']}[{v['A']}]=b[{v['REGS']}[{v['C']}] or {v['GETC']}({v['C']})] end
|
||||
elseif {v['OP']}=={self.opcodes.index('SETTABLE')} then
|
||||
local a={v['REGS']}[{v['A']}];
|
||||
if a then a[{v['REGS']}[{v['B']}] or {v['GETC']}({v['B']})]={v['REGS']}[{v['C']}] end
|
||||
elseif {v['OP']}=={self.opcodes.index('SELF')} then
|
||||
local b={v['REGS']}[{v['B']}];
|
||||
if b then
|
||||
{v['REGS']}[{v['A']}+1]=b;
|
||||
{v['REGS']}[{v['A']}]=b[{v['GETC']}({v['C']})];
|
||||
end
|
||||
elseif {v['OP']}=={self.opcodes.index('ADD')} then {v['REGS']}[{v['A']}]=({v['REGS']}[{v['B']}] or 0)+({v['REGS']}[{v['C']}] or 0)
|
||||
elseif {v['OP']}=={self.opcodes.index('SUB')} then {v['REGS']}[{v['A']}]=({v['REGS']}[{v['B']}] or 0)-({v['REGS']}[{v['C']}] or 0)
|
||||
elseif {v['OP']}=={self.opcodes.index('MUL')} then {v['REGS']}[{v['A']}]=({v['REGS']}[{v['B']}] or 0)*({v['REGS']}[{v['C']}] or 0)
|
||||
elseif {v['OP']}=={self.opcodes.index('DIV')} then {v['REGS']}[{v['A']}]=({v['REGS']}[{v['B']}] or 0)/({v['REGS']}[{v['C']}] or 1)
|
||||
elseif {v['OP']}=={self.opcodes.index('UNM')} then {v['REGS']}[{v['A']}]=-({v['REGS']}[{v['B']}] or 0)
|
||||
elseif {v['OP']}=={self.opcodes.index('NOT')} then {v['REGS']}[{v['A']}]=not {v['REGS']}[{v['B']}]
|
||||
elseif {v['OP']}=={self.opcodes.index('LEN')} then {v['REGS']}[{v['A']}]=#{v['REGS']}[{v['B']}] or 0
|
||||
elseif {v['OP']}=={self.opcodes.index('CALL')} then
|
||||
local f={v['REGS']}[{v['A']}];
|
||||
if f then
|
||||
local args={{}};
|
||||
if {v['B']}>1 then for i=1,{v['B']}-1 do args[i]={v['REGS']}[{v['A']} + i] end end;
|
||||
local res={{f(({v['UNP']})(args))}};
|
||||
if {v['C']}>1 then for i=1,{v['C']}-1 do {v['REGS']}[{v['A']} + i - 1]=res[i] end end;
|
||||
end
|
||||
elseif {v['OP']}=={self.opcodes.index('RETURN')} then break end;
|
||||
end;
|
||||
end;
|
||||
({v['SPW']})({v['EXEC']});'''
|
||||
return self.minify(vm_lua)
|
||||
|
||||
def compile_to_bytecode(self, ast):
|
||||
constants, instructions, locals_map = [], [], {}
|
||||
self.next_reg = 0
|
||||
def add_const(val):
|
||||
if isinstance(val, str) and ((val.startswith("'" ) and val.endswith("'" )) or (val.startswith('"') and val.endswith('"'))): val = val[1:-1]
|
||||
for i, c in enumerate(constants):
|
||||
if c['value'] == val: return i
|
||||
t = 'string' if isinstance(val, str) else 'number'
|
||||
if val in ['true', 'false', 'nil']: t = 'keyword'
|
||||
constants.append({'type': t, 'value': val})
|
||||
return len(constants) - 1
|
||||
def emit(op, a=0, b=0, c=0): instructions.append([self.op_to_id[op], a, b, c])
|
||||
def gen_expr(expr, target):
|
||||
if not expr: return
|
||||
if expr['type'] == 'NUMBER': emit("LOADK", target, add_const(float(expr['value'])))
|
||||
elif expr['type'] == 'STRING': emit("LOADK", target, add_const(expr['value']))
|
||||
elif expr['type'] == 'KEYWORD': emit("LOADK", target, add_const(expr['value']))
|
||||
elif expr['type'] == 'variable':
|
||||
if expr['name'] in locals_map: emit("MOVE", target, locals_map[expr['name']])
|
||||
else: emit("GETGLOBAL", target, add_const(expr['name']))
|
||||
elif expr['type'] == 'index':
|
||||
br = self.next_reg; self.next_reg += 1; gen_expr(expr['base'], br)
|
||||
if expr['key']['type'] == 'STRING':
|
||||
emit("GETTABLE", target, br, add_const(expr['key']['value']))
|
||||
else:
|
||||
kr = self.next_reg; self.next_reg += 1; gen_expr(expr['key'], kr)
|
||||
emit("GETTABLE", target, br, kr); self.next_reg -= 1
|
||||
self.next_reg -= 1
|
||||
elif expr['type'] == 'binary':
|
||||
lr = self.next_reg; self.next_reg += 1; gen_expr(expr['left'], lr)
|
||||
rr = self.next_reg; self.next_reg += 1; gen_expr(expr['right'], rr)
|
||||
op_m = {'+': 'ADD', '-': 'SUB', '*': 'MUL', '/': 'DIV'}
|
||||
emit(op_m.get(expr['op'], 'ADD'), target, lr, rr); self.next_reg -= 2
|
||||
elif expr['type'] == 'unary':
|
||||
or_reg = self.next_reg; self.next_reg += 1; gen_expr(expr['operand'], or_reg)
|
||||
op_m = {'-': 'UNM', '#': 'LEN', 'not': 'NOT'}
|
||||
emit(op_m.get(expr['op'], 'UNM'), target, or_reg); self.next_reg -= 1
|
||||
elif expr['type'] == 'call': gen_call(expr, target)
|
||||
elif expr['type'] == 'method_call': gen_method_call(expr, target)
|
||||
def gen_call(node, target):
|
||||
fr = self.next_reg; self.next_reg += 1
|
||||
gen_expr(node['func'], fr)
|
||||
for i, arg in enumerate(node['args']):
|
||||
ar = self.next_reg; self.next_reg += 1; gen_expr(arg, ar)
|
||||
emit("CALL", fr, len(node['args']) + 1, 2)
|
||||
if target != fr: emit("MOVE", target, fr)
|
||||
self.next_reg = fr
|
||||
def gen_method_call(node, target):
|
||||
fr = self.next_reg; self.next_reg += 1
|
||||
br = self.next_reg; self.next_reg += 1
|
||||
gen_expr(node['base'], br)
|
||||
emit("SELF", fr, br, add_const(node['method']))
|
||||
self.next_reg = fr + 2
|
||||
for i, arg in enumerate(node['args']):
|
||||
ar = self.next_reg; self.next_reg += 1; gen_expr(arg, ar)
|
||||
emit("CALL", fr, len(node['args']) + 2, 2)
|
||||
if target != fr: emit("MOVE", target, fr)
|
||||
self.next_reg = fr
|
||||
for node in ast:
|
||||
if node['type'] == 'assign':
|
||||
vr = self.next_reg; self.next_reg += 1; gen_expr(node['value'], vr)
|
||||
if node.get('local'): locals_map[node['name']] = vr
|
||||
elif 'target' in node:
|
||||
t = node['target']
|
||||
if t['type'] == 'index':
|
||||
br = self.next_reg; self.next_reg += 1; gen_expr(t['base'], br)
|
||||
if t['key']['type'] == 'STRING':
|
||||
emit("SETTABLE", br, add_const(t['key']['value']), vr)
|
||||
else:
|
||||
kr = self.next_reg; self.next_reg += 1; gen_expr(t['key'], kr)
|
||||
emit("SETTABLE", br, kr, vr); self.next_reg -= 1
|
||||
self.next_reg -= 1
|
||||
elif t['type'] == 'variable':
|
||||
emit("SETGLOBAL", vr, add_const(t['name']))
|
||||
else: emit("SETGLOBAL", vr, add_const(node['name']))
|
||||
elif node['type'] == 'call': gen_call(node, self.next_reg)
|
||||
elif node['type'] == 'method_call': gen_method_call(node, self.next_reg)
|
||||
elif node['type'] == 'return':
|
||||
vr = self.next_reg; self.next_reg += 1; gen_expr(node['value'], vr)
|
||||
emit("RETURN", vr, 2)
|
||||
self.next_reg -= 1
|
||||
emit("RETURN")
|
||||
return {"instructions": instructions, "constants": constants}
|
||||
|
||||
def obfuscate(self, code):
|
||||
if not code.strip(): return ""
|
||||
try:
|
||||
lexer = Lexer(code)
|
||||
tokens = lexer.tokenize()
|
||||
parser = Parser(tokens)
|
||||
ast = parser.parse()
|
||||
if not ast: return ""
|
||||
bytecode = self.compile_to_bytecode(ast)
|
||||
return self.generate_vm_source(bytecode)
|
||||
except Exception:
|
||||
import traceback
|
||||
return f"-- Obfuscation Error: {traceback.format_exc()}"
|
||||
|
||||
def obfuscate(code):
|
||||
return LuauVMObfuscator().obfuscate(code)
|
||||
117
core/parser.py
117
core/parser.py
@ -1,117 +0,0 @@
|
||||
import re
|
||||
|
||||
class Lexer:
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
self.tokens = []
|
||||
self.pos = 0
|
||||
LB, RB, DQ, SQ, BS = chr(91), chr(93), chr(34), chr(39), chr(92)
|
||||
OP_LIST = r'==|~=|<=|>=|\.\.\.|\.\.|>>|<<|\+|\-|\*|/|%|\^|#|=|\<|\>|\(|\)|\{|\}|' + BS + LB + '|' + BS + RB + '|;|:|,|\.'
|
||||
self.rules = [
|
||||
('COMMENT', re.compile('--' + LB + LB + '.*?' + RB + RB + '|--.*', re.DOTALL)),
|
||||
('STRING', re.compile(DQ + r'(?:' + BS + BS + r'.|[^' + DQ + BS + BS + r'])*' + DQ + '|' + SQ + r'(?:' + BS + BS + r'.|[^' + SQ + BS + BS + r'])*' + SQ + '|' + LB + LB + '.*?' + RB + RB, re.DOTALL)),
|
||||
('NUMBER', re.compile(r'\b\d+\.?\d*(?:[eE][+-]?\d+)?\b|\b0x[0-9a-fA-F]+\b')),
|
||||
('KEYWORD', re.compile(r'\b(and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b')),
|
||||
('IDENT', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')),
|
||||
('OP', re.compile(OP_LIST)),
|
||||
('SPACE', re.compile(r'\s+'))
|
||||
]
|
||||
def tokenize(self):
|
||||
while self.pos < len(self.code):
|
||||
match = None
|
||||
for name, regex in self.rules:
|
||||
match = regex.match(self.code, self.pos)
|
||||
if match:
|
||||
if name != 'SPACE' and name != 'COMMENT': self.tokens.append((name, match.group(0)))
|
||||
self.pos = match.end(); break
|
||||
if not match: self.pos += 1
|
||||
return self.tokens
|
||||
|
||||
class Parser:
|
||||
def __init__(self, tokens): self.tokens = tokens; self.pos = 0
|
||||
def peek(self, offset=0):
|
||||
idx = self.pos + offset
|
||||
return self.tokens[idx] if idx < len(self.tokens) else (None, None)
|
||||
def consume(self, t=None, v=None):
|
||||
tk = self.peek()
|
||||
if not tk or not tk[0]: return None
|
||||
if t and tk[0] != t: return None
|
||||
if v and tk[1] != v: return None
|
||||
self.pos += 1; return tk
|
||||
def parse(self):
|
||||
nodes = []
|
||||
while self.pos < len(self.tokens):
|
||||
node = self.parse_statement()
|
||||
if node: nodes.append(node)
|
||||
else: self.pos += 1
|
||||
return nodes
|
||||
def parse_statement(self):
|
||||
tk = self.peek()
|
||||
if not tk or not tk[0]: return None
|
||||
if tk[1] == 'local':
|
||||
self.consume(); ident = self.consume('IDENT')
|
||||
if ident:
|
||||
if self.peek()[1] == '=':
|
||||
self.consume(); return {'type': 'assign', 'name': ident[1], 'value': self.parse_expression(), 'local': True}
|
||||
return {'type': 'assign', 'name': ident[1], 'value': {'type': 'KEYWORD', 'value': 'nil'}, 'local': True}
|
||||
return None
|
||||
if tk[1] == 'return':
|
||||
self.consume()
|
||||
return {'type': 'return', 'value': self.parse_expression()}
|
||||
if tk[1] == ';':
|
||||
self.consume(); return None
|
||||
start = self.pos; expr = self.parse_prefix_expression()
|
||||
if expr:
|
||||
if self.peek()[1] == '=':
|
||||
self.consume(); return {'type': 'assign', 'target': expr, 'value': self.parse_expression()}
|
||||
elif expr['type'] in ['call', 'method_call']: return expr
|
||||
self.pos = start; return None
|
||||
def parse_prefix_expression(self):
|
||||
tk = self.peek()
|
||||
if not tk or tk[0] != 'IDENT': return None
|
||||
expr = {'type': 'variable', 'name': self.consume()[1]}
|
||||
while True:
|
||||
nt = self.peek()
|
||||
if nt[1] == '.':
|
||||
self.consume(); m = self.consume('IDENT')
|
||||
if m: expr = {'type': 'index', 'base': expr, 'key': {'type': 'STRING', 'value': '"'+m[1]+'"'}}
|
||||
else: break
|
||||
elif nt[1] == ':':
|
||||
self.consume(); m = self.consume('IDENT')
|
||||
if m:
|
||||
if self.peek()[1] == '(':
|
||||
self.consume(); args = []
|
||||
if self.peek()[1] != ')':
|
||||
args.append(self.parse_expression())
|
||||
while self.peek()[1] == ',': self.consume(); args.append(self.parse_expression())
|
||||
self.consume('OP', ')'); expr = {'type': 'method_call', 'base': expr, 'method': m[1], 'args': args}
|
||||
else: expr = {'type': 'index', 'base': expr, 'key': {'type': 'STRING', 'value': '"'+m[1]+'"'}}
|
||||
else: break
|
||||
elif nt[1] == '[':
|
||||
self.consume(); key = self.parse_expression(); self.consume('OP', ']'); expr = {'type': 'index', 'base': expr, 'key': key}
|
||||
elif nt[1] == '(':
|
||||
self.consume(); args = []
|
||||
if self.peek()[1] != ')':
|
||||
args.append(self.parse_expression())
|
||||
while self.peek()[1] == ',': self.consume(); args.append(self.parse_expression())
|
||||
self.consume('OP', ')'); expr = {'type': 'call', 'func': expr, 'args': args}
|
||||
else: break
|
||||
return expr
|
||||
def parse_expression(self):
|
||||
left = self.parse_primary_expression()
|
||||
if not left: return None
|
||||
while self.peek()[1] in ['+', '-', '*', '/', '..', '==', '<', '>', '<=', '>=', '~=']:
|
||||
op = self.consume()[1]; right = self.parse_primary_expression()
|
||||
if not right: break
|
||||
left = {'type': 'binary', 'op': op, 'left': left, 'right': right}
|
||||
return left
|
||||
def parse_primary_expression(self):
|
||||
tk = self.peek()
|
||||
if not tk: return None
|
||||
if tk[1] in ['-', '#', 'not']:
|
||||
op = self.consume()[1]; sub = self.parse_primary_expression()
|
||||
return {'type': 'unary', 'op': op, 'operand': sub} if sub else None
|
||||
if tk[0] in ['STRING', 'NUMBER']: v = tk[1]; self.consume(); return {'type': tk[0], 'value': v}
|
||||
if tk[1] in ['true', 'false', 'nil']: return {'type': 'KEYWORD', 'value': self.consume()[1]}
|
||||
if tk[1] == '(': self.consume(); e = self.parse_expression(); self.consume('OP', ')'); return e
|
||||
return self.parse_prefix_expression()
|
||||
@ -1,231 +1,145 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ project_name }} | Strong Protection{% endblock %}
|
||||
{% block title %}{{ project_name }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-bg: #0f172a;
|
||||
--secondary-bg: #1e293b;
|
||||
--accent-blue: #38bdf8;
|
||||
--accent-green: #22c55e;
|
||||
--text-main: #f8fafc;
|
||||
--text-muted: #94a3b8;
|
||||
--border-color: #334155;
|
||||
--glass-bg: rgba(30, 41, 59, 0.7);
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes bg-pan {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--primary-bg);
|
||||
color: var(--text-main);
|
||||
font-family: 'Inter', sans-serif;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-image:
|
||||
radial-gradient(circle at 10% 20%, rgba(56, 189, 248, 0.05) 0%, transparent 40%),
|
||||
radial-gradient(circle at 90% 80%, rgba(34, 197, 94, 0.05) 0%, transparent 40%);
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 1rem 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
||||
font-weight: 700;
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 1.5rem auto;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: 700;
|
||||
font-size: 1.25rem;
|
||||
color: var(--accent-blue);
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.runtime code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.logo span { color: var(--text-main); }
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem 2rem;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(to right, var(--accent-blue), var(--accent-green));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
color: var(--text-muted);
|
||||
max-width: 600px;
|
||||
margin: 0 auto 2rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem 4rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.editor-container { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.editor-pane {
|
||||
background: var(--secondary-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
padding: 0.75rem 1rem;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #e2e8f0;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
padding: 1rem;
|
||||
resize: none;
|
||||
height: 400px;
|
||||
outline: none;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.btn-obfuscate {
|
||||
grid-column: span 2;
|
||||
background: linear-gradient(to right, var(--accent-blue), #2563eb);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-top: -1rem;
|
||||
box-shadow: 0 4px 14px 0 rgba(56, 189, 248, 0.39);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) { .btn-obfuscate { grid-column: span 1; } }
|
||||
|
||||
.btn-obfuscate:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(56, 189, 248, 0.45);
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.btn-obfuscate:active { transform: translateY(0); }
|
||||
|
||||
.copy-btn {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-muted);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.copy-btn:hover { color: var(--text-main); border-color: var(--text-muted); }
|
||||
|
||||
.badge {
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.badge-blue { background: rgba(56, 189, 248, 0.1); color: var(--accent-blue); }
|
||||
.badge-green { background: rgba(34, 197, 94, 0.1); color: var(--accent-green); }
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.875rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<nav class="navbar">
|
||||
<a href="/" class="logo">VM-<span>LUAU</span></a>
|
||||
<div>
|
||||
<a href="/admin/" style="color: var(--text-muted); text-decoration: none; font-size: 0.875rem;">Admin Access</a>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your app…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="hero">
|
||||
<h1>Next-Gen Luau Protection</h1>
|
||||
<p>Secure your Roblox scripts with our custom Virtual Machine obfuscation. Harder to reverse-engineer, impossible to ignore.</p>
|
||||
</section>
|
||||
|
||||
<form method="POST" class="editor-container">
|
||||
{% csrf_token %}
|
||||
<div class="editor-pane">
|
||||
<div class="editor-header">
|
||||
<span>INPUT.LUAU</span>
|
||||
<span class="badge badge-blue">Source</span>
|
||||
</div>
|
||||
<textarea name="code" placeholder="-- Paste your Luau code here..." required>{{ input_code }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="editor-pane">
|
||||
<div class="editor-header">
|
||||
<span>PROTECTED.LUAU</span>
|
||||
<button type="button" class="copy-btn" onclick="copyOutput()">Copy Code</button>
|
||||
</div>
|
||||
<textarea id="output-area" readonly placeholder="-- Obfuscated code will appear here...">{{ output_code }}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-obfuscate">Obfuscate Script</button>
|
||||
</form>
|
||||
|
||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
||||
<p class="runtime">
|
||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
© 2026 {{ agent_brand }} | Built with Django & VM Technology
|
||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function copyOutput() {
|
||||
const output = document.getElementById('output-area');
|
||||
output.select();
|
||||
document.execCommand('copy');
|
||||
alert('Copied to clipboard!');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@ -1,37 +1,25 @@
|
||||
import os
|
||||
import platform
|
||||
|
||||
from django import get_version as django_version
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
from .models import ScriptLog
|
||||
from .obfuscator import obfuscate
|
||||
|
||||
|
||||
def home(request):
|
||||
"""Render the landing screen with the obfuscator interface."""
|
||||
output_code = ""
|
||||
input_code = ""
|
||||
|
||||
if request.method == "POST":
|
||||
input_code = request.POST.get("code", "")
|
||||
if input_code:
|
||||
output_code = obfuscate(input_code)
|
||||
|
||||
# Save to log
|
||||
ScriptLog.objects.create(
|
||||
input_code=input_code,
|
||||
output_code=output_code,
|
||||
input_size=len(input_code),
|
||||
output_size=len(output_code)
|
||||
)
|
||||
|
||||
"""Render the landing screen with loader and environment details."""
|
||||
host_name = request.get_host().lower()
|
||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
||||
now = timezone.now()
|
||||
|
||||
context = {
|
||||
"project_name": "VM-Luau Obfuscator",
|
||||
"project_name": "New Style",
|
||||
"agent_brand": agent_brand,
|
||||
"input_code": input_code,
|
||||
"output_code": output_code,
|
||||
"django_version": django_version(),
|
||||
"python_version": platform.python_version(),
|
||||
"current_time": now,
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", "Strong Luau Obfuscation with Custom VM Protection."),
|
||||
"host_name": host_name,
|
||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
||||
}
|
||||
return render(request, "core/index.html", context)
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
from core.obfuscator import obfuscate
|
||||
|
||||
code = """
|
||||
local a = 10
|
||||
local b = 20
|
||||
local c = a + b
|
||||
print("Sum is:", c)
|
||||
"""
|
||||
|
||||
obfuscated = obfuscate(code)
|
||||
print(obfuscated)
|
||||
|
||||
# Check for some patterns
|
||||
if "_l1" in obfuscated or "_I0" in obfuscated:
|
||||
print("SUCCESS: Variable renaming detected.")
|
||||
if "bit32.bxor" in obfuscated or "+" in obfuscated or "-" in obfuscated:
|
||||
# Check if it's in the VM logic, not just the original code (which might have +)
|
||||
# The VM logic uses to_expr for indices and keys.
|
||||
print("SUCCESS: Number expressions detected.")
|
||||
if "math.pi" in obfuscated or "math.sqrt" in obfuscated:
|
||||
print("SUCCESS: Opaque predicates detected.")
|
||||
Loading…
x
Reference in New Issue
Block a user