Wirtualizuj i obfuskuj skrypty Lua .lua dzięki maszynie wirtualnej, skończonym automacie stanów (FSM), taśmie splotu albo spłaszczonemu przepływowi oraz kontroli integralności — przez GUI, CLI, narzędzie online lub API. Automatyczne wykrywanie dialektu obejmuje Lua 5.1–5.5 i Luau, a LuaJIT na żądanie.
Lua Virtualizer chroni własnościowe skrypty .lua. Przebudowuje instrukcje na maszynę wirtualną, skończony automat stanów (FSM), taśmę splotu albo spłaszczony przepływ, a potem zmienia nazwy identyfikatorów, szyfruje ciągi i liczby, sprawdza własną integralność, dokleja fałszywe wywołania i szuka debuggerów oraz środowisk analizy.
Lua to język, w którym naprawdę wychodzą gry, urządzenia, OpenResty i nginx, skrypty Redis oraz wtyczki aplikacji. Na dysku leży źródło. Kto otworzy plik, przeczyta algorytm, test licencji i każdy sekret zapisany jako ciąg znaków.
Reguły gry, polityka urządzenia, kontrola dostępu w OpenResty, skrypt Redis albo wtyczka to często jeden czytelny plik .lua obok programu, który go ładuje. Wygodnie się go aktualizuje i równie wygodnie kopiuje. Testy licencji, wzory cen i tokeny siedzą w tym samym pliku co reszta logiki.
Kompilacja luac albo wysyłka bajtkodu LuaJIT lub Luau nie jest ochroną. Publiczne dekompilatory odtwarzają z tego bajtkodu listing, który da się czytać. Ochrona musi zmienić program, a nie tylko format pliku.
Wiele wydanych gier trzyma reguły, interfejs i mody w Lua. Skrypt obok gry jest tą częścią, którą gracze i inni twórcy mogą otworzyć. Kilka najszerzej granych tytułów, które ładują Lua:
Wybrane funkcje kompilują się do prywatnego strumienia instrukcji wewnątrz powłoki Lua 5.1. Wirtualizacja kodu trzyma obraz bajtkodu tylko do odczytu i tabelę handlerów. Skończony automat stanów (FSM) przesuwa nieprzezroczystą bramkę wokół pobrania. Splot zostawia ten sam zaszyfrowany blob, a potem działa z jednej mutowalnej taśmy rejestrów, stałych i słów instrukcji, z trzema superoperatorami i bez mapy opcode. Spłaszczony przepływ używa tego samego obrazu i miesza bramkę z bieżącą sumą kontrolną. Host nadal ładuje zwykły chunk Lua.
Lua Virtualizer zmienia nazwy zmiennych lokalnych, parametrów i funkcji, szyfruje ciągi i liczby, dzieli literały i wstawia pule przynęt. Chroniony skrypt zachowuje przetestowane zachowanie i ukrywa literały oraz nazwy, po których oryginał łatwo było śledzić.
Skończony automat stanów (FSM) zostawia tabelę handlerów i opiera nieprzezroczystą bramkę na wirtualnym liczniku programu. Spłaszczony przepływ opiera tę bramkę na liczniku zmieszanym z bieżącą sumą kontrolną. Skoki gościa w obu trybach nadal zapisują bezwzględny licznik programu do obrazu kodu.
Opcjonalne sondy szukają podłączonego debuggera, maszyny wirtualnej, piaskownicy albo emulatora CPU. Gdy sonda pasuje, skrypt kończy pracę po cichu, zamiast iść dalej w sesji interaktywnej.
Sonda startowa odciska załadowany chunk i wyprowadza z tego tokenu późniejsze klucze. Podmieniony skrypt już nie pasuje, więc zaszyfrowane wartości wracają jako śmieci zamiast oryginalnego tekstu.
Linker ochrony dodaje funkcje-przynęty i fałszywe wywołania, więc skopiowany fragment nadal wygląda jak kod programu. Te pułapki milczą, gdy skrypt startuje normalnie. Mają znaczenie, gdy ktoś wytnie kawałek i uruchomi go osobno.
Otwórz zakładkę „Po wirtualizacji”, aby zobaczyć ten sam skrypt przebudowany z każdą ochroną ustawioną na maksimum. Listing to pierwsze 100 linii:
local function greet(name)
print("Hello from " .. name)
end
local function clamp(n, lo, hi)
if n < lo then return lo end
if n > hi then return hi end
return n
end
local function total(price, qty, tax)
local net = price * qty
return net + net * tax
end
local function names(rows)
local out = {}
for i = 1, #rows do
out[i] = rows[i].name .. ":" .. tostring(rows[i].score)
end
return table.concat(out, ", ")
end
local function band(n)
if n < 10 then return "low" end
if n < 50 then return "mid" end
return "high"
end
local function slug(text)
return string.lower(string.gsub(text, " ", "-"))
end
local users = {
{ name = "Ada", score = 10 },
{ name = "Lin", score = 7 },
{ name = "Kai", score = 19 },
}
greet("Lua Virtualizer")
print(clamp(12, 0, 9))
print(string.format("%.2f", total(19.99, 3, 0.23)))
print(names(users))
print(band(42))
print(slug("Lua Virtualizer"))
local function factorial(n)
local r = 1
for i = 2, n do
r = r * i
end
return r
end
local function best(list)
local top = list[1]
for i = 2, #list do
if list[i] > top then top = list[i] end
end
return top
end
print(factorial(6))
print(best({ 12, 7, 19, 4 }))
local DhfvYC
local j7eLFM
local function ERbe6d()
end
local function YZcPXb(KZc4LW, X4DFT)
local aUeSOA, VmvhF = 0, 1
for gueTwb = 1, 8 do
local vucK6n, kHfJv9 = (KZc4LW % 2), (X4DFT % 2)
if (vucK6n ~= kHfJv9) then
aUeSOA = (aUeSOA + VmvhF)
end
KZc4LW = ((KZc4LW - vucK6n) / 2)
X4DFT = ((X4DFT - kHfJv9) / 2)
VmvhF = (VmvhF * 2)
end
return aUeSOA
end
local wafzN0 = {138, 229, 185, 124, 93, 49, 96, 170, 154, 197, 223, 191, 139, 173, 105, 30, 56, 4, 176, 103, 73, 199, 101, 14, 132, 167, 112, 98, 20, 94, 22, 58, 217, 157, 69, 75, 230, 104, 228, 60, 241, 8, 186, 54, 71, 169, 183, 40, 111, 85, 117, 181, 32, 198, 36, 254, 187, 195, 231, 226, 161, 63, 214, 135, 147, 23, 116, 6, 125, 141, 207, 38, 67, 107, 78, 48, 35, 215, 209, 188, 238, 251, 193, 127, 205, 246, 37, 91, 218, 242, 153, 42, 203, 235, 70, 216, 15, 144, 89, 7, 248, 196, 121, 194, 192, 156, 164, 236, 253, 200, 152, 239, 171, 204, 220, 222, 113, 72, 158, 114, 25, 5, 126, 44, 172, 130, 52, 108, 225, 81, 33, 190, 28, 162, 79, 11, 74, 76, 168, 227, 165, 219, 1, 62, 178, 255, 159, 59, 129, 3, 12, 109, 0, 99, 95, 66, 80, 88, 90, 213, 31, 45, 51, 97, 202, 2, 224, 123, 39, 136, 43, 83, 148, 252, 142, 10, 87, 24, 106, 166, 65, 250, 19, 77, 189, 55, 27, 82, 9, 208, 177, 61, 119, 210, 182, 146, 47, 34, 149, 237, 244, 221, 41, 240, 122, 84, 128, 131, 26, 174, 17, 29, 175, 50, 53, 68, 46, 92, 86, 137, 18, 150, 180, 179, 155, 211, 102, 234, 16, 140, 120, 57, 115, 100, 232, 110, 143, 233, 160, 13, 247, 151, 245, 212, 243, 134, 145, 64, 201, 184, 163, 249, 118, 206, 133, 21}
local function qPc9kW(sCbnqN, hVbAwZ, xkbmZY)
local sseXe6 = {}
local JIc56K = hVbAwZ
for SFcSJ3 = 1, (#sCbnqN) do
sseXe6[SFcSJ3] = DhfvYC.char(((sCbnqN:byte(SFcSJ3) - JIc56K) % 256))
JIc56K = ((JIc56K + xkbmZY) % 251)
end
return j7eLFM.concat(sseXe6)
end
local function LkbYjc(sCbnqN, hVbAwZ, xkbmZY)
local sseXe6 = {}
local JIc56K = hVbAwZ
for SFcSJ3 = 1, (#sCbnqN) do
sseXe6[SFcSJ3] = DhfvYC.char(YZcPXb(sCbnqN:byte(SFcSJ3), JIc56K))
JIc56K = ((JIc56K + xkbmZY) % 251)
end
return j7eLFM.concat(sseXe6)
end
local function sHcgH9(sCbnqN)
return DhfvYC.reverse(sCbnqN)
end
local function b5fzDj(sCbnqN)
local sseXe6 = {}
for SFcSJ3 = 1, (#sCbnqN) do
sseXe6[SFcSJ3] = DhfvYC.char(wafzN0[(sCbnqN:byte(SFcSJ3) + 1)])
end
return j7eLFM.concat(sseXe6)
end
DhfvYC = _G[((((("s" .. "t") .. "r") .. "i") .. "n") .. "g")]
j7eLFM = _G[(((("t" .. "a") .. "b") .. "l") .. "e")]
local tAbmhU = _G[LkbYjc(sHcgH9"n\157\176\212", 185, 24)]
local zJb9BT = _G[LkbYjc(b5fzDj"\177\208\145g_8", 107, 20)]
local A4chI0 = _G[LkbYjc(b5fzDj"p\229\006H", 223, 22)]
local RzQpa = _G[qPc9kW(b5fzDj(sHcgH9"\020\160Q\243\238"), 59, 39)]
local lxcw3 = _G[qPc9kW(LkbYjc(b5fzDj"T-\195\192!\007\246> \222\136\134", 226, 11), 200, 23)]
local zFfoQl = _G[sHcgH9(LkbYjc(b5fzDj"\211\186o(s\243\179|", 111, 15))]
local YIbgWE = (_G[qPc9kW(LkbYjc("=\014\006\022\014\012", 37, 25), 163, 31)] or j7eLFM.unpack)
local Leenf9 = zFfoQl(LkbYjc(b5fzDj(sHcgH9"\203EZ"), 171, 13))
local J8b0XE = zFfoQl(LkbYjc(sHcgH9(b5fzDj"\022S\135\186\202\170\005\164\243\199"), 217, 13))
local v4cFQZ = zFfoQl(b5fzDj(sHcgH9"\016~+\162\016~\185~\005\213"))
local l1d9rX = zFfoQl(qPc9kW(sHcgH9(LkbYjc("\029\025Z_\158TY\001PZ", 160, 20)), 207, 20))
local gVcBY6 = zFfoQl(qPc9kW(LkbYjc(sHcgH9"+\\HFC", 223, 3), 102, 9))
local R8dW75 = zFfoQl(LkbYjc(b5fzDj(qPc9kW("\165'\197", 111, 32)), 22, 31))
local ZLboDx = zFfoQl(qPc9kW(b5fzDj(LkbYjc("&\141Kj\211\137?\180", 162, 35)), 235, 29))
local FTbf2z = zFfoQl(qPc9kW(LkbYjc(b5fzDj"\144\012?", 154, 11), 246, 3))
local function w4e0sp(MLbD1J, Yofdl7)
local AZchOy, E4s89 = 0, 1
for IZbwEa = 1, 8 do
local iCcbJe, qtnql = (MLbD1J % 2), (Yofdl7 % 2)
if (iCcbJe ~= qtnql) then
AZchOy = (AZchOy + E4s89)
end
MLbD1J = ((MLbD1J - iCcbJe) / 2)
Yofdl7 = ((Yofdl7 - qtnql) / 2)
E4s89 = (E4s89 * 2)
end
return AZchOy
end
local function Zhd7Lu(XYfwLf)
if tAbmhU.tointeger then
local BmcGzJ = tAbmhU.tointeger(XYfwLf)
if (BmcGzJ ~= nil) then
return BmcGzJ
end
end
return XYfwLf
end
local function igc6L5(XYfwLf)
XYfwLf = (XYfwLf % J8b0XE)
if (XYfwLf >= v4cFQZ) then
XYfwLf = (XYfwLf - J8b0XE)
end
return Zhd7Lu(XYfwLf)
end
local function judllp(XYfwLf)
XYfwLf = igc6L5(XYfwLf)
if (XYfwLf < 0) then
XYfwLf = (XYfwLf + J8b0XE)
end
return XYfwLf
end
local function o7e0Ok(xqdf1D, c5bHpu, gyePF9)
Czy nadal rozpoznałbyś oryginalny skrypt, gdyby ten listing był jedyną kopią, którą masz?
Silnik parsuje skrypt, wirtualizuje wybrane fragmenty i może zostawić ten kod jako prywatny bajtkod wewnątrz przenośnego interpretera Lua 5.1 dołączonego do wyniku. Wybrane przebiegi obfuskacji idą wokół tej wirtualizacji. Host nadal ładuje zwykły chunk Lua. Interpreter jest po to, żeby chroniona logika nie zależała od prywatnego zestawu opcode'ów w hoście.
Automatyczne wykrywanie dialektu próbuje źródła jako Lua 5.1, potem 5.2, 5.3, 5.4, 5.5, a następnie Luau i zostawia najstarszy dialekt, który się parsuje. LuaJIT nie jest wnioskowany. Wybierz LuaJIT, gdy skrypt ma być traktowany jako LuaJIT. Skrypt, którego jedyną nowszą cechą jest goto, jest wykrywany jako 5.2, bo goto pojawiło się w Lua 5.2.
Zawsze uruchom chroniony plik w środowisku, które wysyłasz — PUC-Rio Lua, LuaJIT, Luau, OpenResty albo Redis — zanim podmienisz oryginał.
Lua Virtualizer zawiera klienta wiersza poleceń dla Windows i Linuksa. Wskaż skrypt, raport JSON i plik chroniony:
lua-virtualizer -i input.lua -o result.json --output-script protected.lua
lua-virtualizer -i input.lua -o result.json --output-script protected.lua --target luajit
-i to skrypt wejściowy. -o zapisuje raport JSON. --output-script zapisuje chroniony plik .lua. Pomiń --target, aby wykryć dialekt automatycznie, albo podaj 5.1, 5.2, 5.3, 5.4, 5.5, luajit albo luau. Użyj klienta ze skryptu budowania albo kroku pakowania.
Jeśli chcesz zapytać o Lua Virtualizer, lub coś jest niejasne, napisz do mnie.