Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # obfuscate.py
- # Toy Lua packer/obfuscator using a "Clock-Direction RNG" driftwheel keystream.
- # Usage:
- # python obfuscate.py input.lua output_obf.lua --seed 7 --rounds 9
- from __future__ import annotations
- import argparse
- import base64
- from dataclasses import dataclass
- # ---------- Driftwheel RNG (Python side, mirrors Lua) ----------
- @dataclass
- class DriftState:
- shape: int # 0..5
- color: int # 0..5
- direction: int # 1..12
- # 12 clock positions -> angle bucket + base "shape/color" impulses (integers)
- DIRECTIONS = {
- 1: (1, 0),
- 2: (2, 1),
- 3: (3, 2),
- 4: (4, 3),
- 5: (5, 4),
- 6: (0, 5),
- 7: (1, 4),
- 8: (2, 3),
- 9: (3, 2),
- 10: (4, 1),
- 11: (5, 0),
- 12: (0, 1),
- }
- def _mix(a: int, b: int) -> int:
- # small nonlinear mixer in 0..255 space
- x = (a * 73 + b * 151 + 19) & 0xFF
- x ^= ((x << 3) & 0xFF)
- x ^= (x >> 5)
- return x & 0xFF
- def _step(st: DriftState, i: int) -> int:
- base_shape, base_color = DIRECTIONS[st.direction]
- # "morph" shape and color using recursive drift
- st.shape = (st.shape + base_shape + (i * 3)) % 6
- st.color = (st.color + base_color + (i * 5)) % 6
- # derive a pseudo "area" value from shape/color/direction
- area = (st.shape + 1) * (st.color + 2) * (st.direction + 7)
- area = _mix(area & 0xFF, (area >> 8) & 0xFF)
- # drift direction (clock step with feedback)
- drift = ((area % 11) + 1) # 1..12-ish
- st.direction = ((st.direction + drift + (i * 3)) % 12) or 12
- return area
- def keystream(seed: int, n: int, rounds: int) -> bytes:
- st = DriftState(shape=0, color=0, direction=((seed - 1) % 12) + 1)
- out = bytearray()
- i = 0
- while len(out) < n:
- # each byte comes from several drift rounds to add diffusion
- v = 0
- for _ in range(rounds):
- v = _mix(v, _step(st, i))
- i += 1
- out.append(v)
- return bytes(out)
- def xor_bytes(data: bytes, ks: bytes) -> bytes:
- return bytes(b ^ ks[i % len(ks)] for i, b in enumerate(data))
- # ---------- Lua template ----------
- LUA_LOADER_TEMPLATE = r'''-- Generated Toy Obfuscator (Clock-Drift Packer)
- -- NOT real security. Reversible. For basic IP deterrence.
- local b64 = [[{B64_PAYLOAD}]]
- local SEED = {SEED}
- local ROUNDS = {ROUNDS}
- local function b64dec(data)
- local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
- data = data:gsub('[^'..b..'=]', '')
- return (data:gsub('.', function(x)
- if x == '=' then return '' end
- local r,f='',(b:find(x)-1)
- for i=6,1,-1 do
- r=r..(f%2^i - f%2^(i-1) > 0 and '1' or '0')
- end
- return r
- end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
- if #x ~= 8 then return '' end
- local c=0
- for i=1,8 do
- c=c + (x:sub(i,i)=='1' and 2^(8-i) or 0)
- end
- return string.char(c)
- end))
- end
- -- Clock-Direction Driftwheel RNG (Lua side)
- local DIRECTIONS = {
- [1]={1,0}, [2]={2,1}, [3]={3,2}, [4]={4,3}, [5]={5,4}, [6]={0,5},
- [7]={1,4}, [8]={2,3}, [9]={3,2}, [10]={4,1}, [11]={5,0}, [12]={0,1},
- }
- local function mix(a,b)
- local x = (a*73 + b*151 + 19) % 256
- x = (x ~ ((x << 3) % 256)) % 256
- x = (x ~ (x >> 5)) % 256
- return x % 256
- end
- local function step(st, i)
- local base = DIRECTIONS[st.dir]
- local baseShape, baseColor = base[1], base[2]
- st.shape = (st.shape + baseShape + (i*3)) % 6
- st.color = (st.color + baseColor + (i*5)) % 6
- local area = (st.shape+1) * (st.color+2) * (st.dir+7)
- area = mix(area % 256, math.floor(area / 256) % 256)
- local drift = (area % 11) + 1
- st.dir = ((st.dir + drift + (i*3)) % 12)
- if st.dir == 0 then st.dir = 12 end
- return area
- end
- local function keystream(seed, n, rounds)
- local st = { shape=0, color=0, dir=((seed-1) % 12)+1 }
- local out = {}
- local i, v = 0, 0
- for k=1,n do
- v = 0
- for _=1,rounds do
- v = mix(v, step(st, i))
- i = i + 1
- end
- out[k] = string.char(v)
- end
- return table.concat(out)
- end
- local function xor_str(data, ks)
- local out = {}
- local klen = #ks
- for i=1,#data do
- local db = data:byte(i)
- local kb = ks:byte(((i-1) % klen) + 1)
- out[i] = string.char(db ~ kb)
- end
- return table.concat(out)
- end
- local enc = b64dec(b64)
- local ks = keystream(SEED, math.max(64, #enc), ROUNDS)
- local src = xor_str(enc, ks)
- local fn, err = load(src, "clock_drift_payload", "t", _G)
- if not fn then error("decode/load failed: "..tostring(err)) end
- return fn()
- '''
- def main() -> None:
- ap = argparse.ArgumentParser()
- ap.add_argument("input", help="Input Lua file")
- ap.add_argument("output", help="Output obfuscated Lua file")
- ap.add_argument("--seed", type=int, default=7, help="Seed direction (1..12 recommended)")
- ap.add_argument("--rounds", type=int, default=9, help="Rounds per keystream byte")
- args = ap.parse_args()
- with open(args.input, "rb") as f:
- src = f.read()
- ks = keystream(args.seed, max(64, len(src)), args.rounds)
- enc = xor_bytes(src, ks)
- b64 = base64.b64encode(enc).decode("ascii")
- out = LUA_LOADER_TEMPLATE.format(
- B64_PAYLOAD=b64,
- SEED=int(args.seed),
- ROUNDS=int(args.rounds),
- )
- with open(args.output, "w", encoding="utf-8") as f:
- f.write(out)
- print(f"OK: wrote {args.output} (seed={args.seed}, rounds={args.rounds})")
- if __name__ == "__main__":
- main()
Add Comment
Please, Sign In to add comment