Quick actions

cmd+k|ctrl+k

Navigation

Languages

Herkle Deobfuscation

Snippet info

Language

Lua

Visibility

public

Author

herkle

Created

2026-01-07T21:07:03.950711Z

Updated

2026-01-30T15:26:35.601885Z

-- This will no longer get update because it's private from now on

-- settings

local getfenvhook = false
local concathook = false
local constant = false -- sucks
local decode = false -- sucks either
local loadshook = false -- prints the whole original source (only if obfuscator uses loadstring)
local pairshook = false -- logs for i,v in pairs

local log = true -- something like threads (not deobfuscate, logs what script does)
local printf = false -- not recommended, may spam your console (on wearedevs)

-- functions for lua 53+ and for deobf

local object_map = {} 
local obj_count = 0
local old_pr = print

if not loadstring then
    function loadstring(code)
        old_pr(code)
        return load(code)
    end
end

function typeof(v)
    return type(v)
end

function getfenv(level)
    level = (level or 1) + 1 
    local info = debug.getinfo(level, "f")
    if not info then return _ENV end
    local func = info.func
    if type(func) ~= "function" then return _ENV end
    local i = 1
    while true do
        local name, value = debug.getupvalue(func, i)
        if not name then break end
        if name == "_ENV" then
            return value
        end
        i = i + 1
    end
    return _ENV
end

function unpack(arg)
    return table.unpack(arg)
end

local function const(source)
    local constants = {}
    local index = {}
    local count = 0

    local function decode(str)
        return (str:gsub("\\(%d%d?%d?)", function(d)
            return string.char(tonumber(d))
        end))
    end

    source = source:gsub("(['\"])(.-)%1", function(_, str)
        local decoded = decode(str)

        if not index[decoded] then
            count = count + 1
            index[decoded] = count
            constants[count] = decoded
        end
        return ("C[%d]"):format(index[decoded])
    end)

    local constSrc = "local C = {\n"
    for i, v in ipairs(constants) do
        constSrc = constSrc .. (" [%d] = %q,\n"):format(i, v)
    end
    constSrc = constSrc .. "}\n\n"

    return constSrc .. source
end

local function addCompoundSupport(code)
    local lines = {}
    for line in code:gmatch("([^\n\r]+)") do
        local startPos, endPos = line:find("%s*%+=%s*")
        if startPos then
            local left = line:sub(1, startPos - 1):match("^%s*(.-)%s*$")
            local right = line:sub(endPos + 1):match("^%s*(.-)%s*$")
            line = string.format("local _v = %s; %s = _v + %s", left, left, right)
        end
        table.insert(lines, line)
    end
    return table.concat(lines, "\n")
end

if log then
local function serialize(v)
    if object_map[v] then return object_map[v] end
    
    local t = (type(v) == "table" and v.__type) or type(v)
    if getmetatable(v) and getmetatable(v).__type == "Instance" then t = "Instance" end

    if t == "string" then 
        return string.format("%q", v)
    elseif t == "EnumItem" then 
        return v.Value 
    elseif t == "Vector3" then 
        return "Vector3.new(" .. tostring(v) .. ")"
    elseif t == "Vector2" then 
        return "Vector2.new(" .. tostring(v) .. ")"
    elseif t == "CFrame" then 
        return "CFrame.new(" .. tostring(v) .. ")"
    elseif t == "Color3" then 
        local constructor = v.__mode or "new" 
        return "Color3." .. constructor .. "(" .. tostring(v) .. ")"
    elseif t == "ColorSequence" then
        local kps = {}
        for i, kp in ipairs(v.Keypoints) do
            table.insert(kps, serialize(kp))
        end
        return "ColorSequence.new({" .. table.concat(kps, ", ") .. "})"
    elseif t == "ColorSequenceKeypoint" then
        return "ColorSequenceKeypoint.new(" .. v.Time .. ", " .. serialize(v.Value) .. ")"
    elseif t == "UDim2" then 
        return "UDim2.new(" .. tostring(v) .. ")"
    elseif t == "UDim" then 
        return "UDim.new(" .. tostring(v) .. ")"
    elseif t == "Instance" then 
        return v:GetFullName()
    elseif t == "table" then 
    local s = "{"
    for k, val in pairs(v) do
        if k ~= "__type" and k ~= "__mode" then
            local keyStr = type(k) == "string" and string.format("%q", k) or tostring(k)
            local valStr = serialize(val)
            s = s .. string.format("\n    [%s] = %s,", keyStr, valStr)
        end
    end
    return s .. "\n}"
    elseif t == "function" then 
        return "function" 
    elseif t == "TweenInfo" then
        return string.format("TweenInfo.new(%s, %s, %s, %s, %s, %s)", v.Time, serialize(v.EasingStyle), serialize(v.EasingDirection), v.RepeatCount, v.Reverses, v.DelayTime)
    else 
        return tostring(v) 
    end
end

local function createDataType(name, methods)
    return setmetatable({}, {
        __index = function(_, method)
            return function(...)
                local args = {...}
                local out = {}
                for i, v in ipairs(args) do out[i] = serialize(v) end
                
                local result = methods[method](...)
                return result
            end
        end
    })
end

_G.Enum = setmetatable({}, {
    __index = function(self, enumType)
        local enumObj = setmetatable({}, {
            __index = function(_, enumItem) 
                return {
                    __type = "EnumItem", 
                    Value = "Enum." .. enumType .. "." .. enumItem 
                } 
            end,
            __tostring = function() return "Enum." .. enumType end
        })
        rawset(self, enumType, enumObj)
        return enumObj
    end
})

_G.Color3 = createDataType("Color3", {
    new = function(r, g, b) 
        return setmetatable({
            R = r or 0, 
            G = g or 0, 
            B = b or 0, 
            __type = "Color3",
            __mode = "new"
        }, {
            __tostring = function(t) return t.R .. ", " .. t.G .. ", " .. t.B end
        }) 
    end,
    fromRGB = function(r, g, b) 
        return setmetatable({
            R = r or 0, 
            G = g or 0, 
            B = b or 0, 
            __type = "Color3",
            __mode = "fromRGB"
        }, {
            __tostring = function(t) return t.R .. ", " .. t.G .. ", " .. t.B end
        })
    end
})

_G.ColorSequenceKeypoint = createDataType("ColorSequenceKeypoint", {
    new = function(time, color)
        return setmetatable({
            Time = time or 0,
            Value = color,
            __type = "ColorSequenceKeypoint"
        }, {
            __tostring = function(t) return tostring(t.Time) .. " " .. serialize(t.Value) end
        })
    end
})

_G.ColorSequence = createDataType("ColorSequence", {
    new = function(...)
        local args = {...}
        local kps = type(args[1]) == "table" and args[1] or args
        return setmetatable({
            Keypoints = kps,
            __type = "ColorSequence"
        }, {
            __tostring = function() return "ColorSequence" end
        })
    end
})

_G.Vector3 = createDataType("Vector3", {
    new = function(x, y, z) return setmetatable({X = x or 0, Y = y or 0, Z = z or 0, __type = "Vector3"}, {__tostring = function(t) return t.X .. ", " .. t.Y .. ", " .. t.Z end}) end
})

_G.Vector2 = createDataType("Vector2", {
    new = function(x, y) 
        return setmetatable({
            X = x or 0, 
            Y = y or 0, 
            __type = "Vector2"
        }, {
            __tostring = function(t) 
                return t.X .. ", " .. t.Y 
            end
        }) 
    end
})

_G.CFrame = createDataType("CFrame", {
    new = function(x, y, z) 
        return setmetatable({p = _G.Vector3.new(x, y, z), __type = "CFrame"}, {
            __tostring = function(t) return tostring(t.p) end
        }) 
    end
})

_G.UDim = createDataType("UDim", {
    new = function(scale, offset)
        return setmetatable({
            Scale = scale or 0,
            Offset = offset or 0,
            __type = "UDim"
        }, {
            __tostring = function(t)
                return t.Scale .. ", " .. t.Offset
            end
        })
    end
})

_G.UDim2 = createDataType("UDim2", {
    new = function(sx, ox, sy, oy) return setmetatable({Sx = sx or 0, Ox = ox or 0, Sy = sy or 0, Oy = oy or 0, __type = "UDim2"}, {__tostring = function(t) return t.Sx .. ", " .. t.Ox .. ", " .. t.Sy .. ", " .. t.Oy end}) end,
    fromScale = function(x, y) return _G.UDim2.new(x, 0, y, 0) end,
    fromOffset = function(x, y) return _G.UDim2.new(0, x, 0, y) end
})

_G.TweenInfo = createDataType("TweenInfo", {
    new = function(time, easingStyle, easingDirection, repeatCount, reverses, delay)
        return setmetatable({
            Time = time or 1,
            EasingStyle = easingStyle or _G.Enum.EasingStyle.Linear,
            EasingDirection = easingDirection or _G.Enum.EasingDirection.Out,
            RepeatCount = repeatCount or 0,
            Reverses = reverses or false,
            DelayTime = delay or 0,
            __type = "TweenInfo"
        }, {
            __tostring = function() return "TweenInfo.new()" end
        })
    end
})

_G.Drawing = {
    new = function(className)
        obj_count = obj_count + 1
        local varName = "draw_" .. obj_count
        
        old_pr(string.format("local %s = Drawing.new(%q)", varName, className))
        
        local internal = { Visible = false, Transparency = 1, Color = _G.Color3.new(1,1,1) }
        local proxy = setmetatable({}, {
            __type = "Drawing",
            __index = function(_, k)
                if k == "Remove" or k == "Destroy" then
                    return function() old_pr(varName .. ":Remove()") end
                end
                return internal[k]
            end,
            __newindex = function(_, k, v)
                internal[k] = v
                old_pr(string.format("%s.%s = %s", varName, k, serialize(v)))
            end
        })
        
        object_map[proxy] = varName
        return proxy
    end
}

local services = {}
local function createInstance(className, name, isService)
    obj_count = obj_count + 1
    local varName = isService and ('game:GetService("' .. name .. '")') or ("obj_" .. obj_count)
    local internal = { ClassName = className, Name = name or className, Parent = nil }

    local proxy = setmetatable({}, {
        __type = "Instance",
        __index = function(_, k)
            if internal[k] then 
                return internal[k] 
            end
            if k == "WalkSpeed" then return 16 end
            if k == "Health" then return 100 end
            if k == "GetFullName" then return function() return "game." .. internal.Name end end
            if k == "WaitForChild" or k == "FindFirstChild" or k == "FindFirstChildOfClass" then 
                return function(_, childName) 
                    local child = createInstance("Part", childName or "Child")
                    old_pr(string.format("local %s = %s:%s(%q)", object_map[child], varName, k, tostring(childName)))
                    return child
                end 
            end
            if k == "SetCore" then
                return function(_, coreName, data)
                    old_pr(string.format("%s:SetCore(%q, %s)", varName, coreName, serialize(data)))
                    return nil
                end
            end
            if k == "GetChildren" or k == "getChildren" then
                return function() return {} end
            end
            if k == "Destroy" or k == "Clone" then
                return function()
                    old_pr(varName .. ":" .. k .. "()")
                    internal.Parent = nil
                end
            end
            if k == "JSONEncode" then
                return function(_, data)
                    local res = "{}"
                    old_pr(string.format('%s:JSONEncode(%s)', varName, serialize(data)))
                    return res
                end
            end
            if k == "GenerateGUID" then
                return function(_, arg)
                    local res = "00000000-0000-0000-0000-000000000000"
                    old_pr(string.format('%s:GenerateGUID(%s)', varName, arg ~= nil and tostring(arg) or ""))
                    return res
                end
            end
            if k == "Create" and internal.ClassName == "TweenService" then
                return function(_, obj, info, props)
                    obj_count = obj_count + 1
                    local tweenVar = "obj_" .. obj_count
                    old_pr(string.format("local %s = %s:Create(%s, %s, %s)", 
                        tweenVar, varName, serialize(obj), serialize(info), serialize(props)))
                        
                    local tweenProxy = {
                        Play = function() old_pr(tweenVar .. ":Play()") end,
                        Stop = function() old_pr(tweenVar .. ":Stop()") end,
                        Cancel = function() old_pr(tweenVar .. ":Cancel()") end,
                        Pause = function() old_pr(tweenVar .. ":Pause()") end,
                        Completed = { Connect = function() return {Disconnect = function() end} end, Wait = function() end }
                    }
                    return tweenProxy
                end
            end
            local propertyObj = createInstance("Part", k)
            old_pr(string.format("local %s = %s.%s", tostring(object_map[propertyObj]), tostring(varName), tostring(k)))

            local mt = getmetatable(propertyObj)
            local old_idx = mt.__index
            
            mt.__index = function(t, key)
                if key == "Connect" or key == "connect" then
                    return function(_, fn)
                        old_pr(object_map[t] .. ":" .. key .. "(function()")
                        if type(fn) == "function" then task.spawn(fn) end
                        old_pr("end)")
                        
                        return { Disconnect = function() end }
                    end
                elseif key == "Wait" or key == "wait" then
                    return function() return end
                end
                return old_idx(t, key)
            end

            rawset(internal, k, propertyObj)
            return propertyObj
        end,
        __newindex = function(proxy, k, v)
            internal[k] = v
            local valStr = serialize(v)
            if (type(v) == "table" or type(v) == "userdata") and valStr == "nil" then
                valStr = "UnknownObject"
            end
            old_pr(varName .. "." .. tostring(k) .. " = " .. valStr)
        end,
        __tostring = function() return internal.Name end
    })
    object_map[proxy] = varName
    return proxy
end

_G.game = setmetatable({ 
    GetService = function(_, s) 
        if not services[s] then services[s] = createInstance(s, s, true) end 
        return services[s] 
    end,
    HttpGet = function(_, url)
        old_pr(string.format('loadstring(game:HttpGet(%q))()', url))
        return ""        
    end
}, {
    __index = function(t, k)
        return t:GetService(k)
    end
})
_G.Instance = { 
    new = function(c, p) 
        local o = createInstance(c)
        old_pr("local " .. object_map[o] .. " = Instance.new(" .. serialize(c) .. ")")
        if p then o.Parent = p end
        return o
    end 
}

_G.game:GetService("UserInputService")
_G.workspace = _G.game:GetService("Workspace")
_G.HttpService = _G.game:GetService("HttpService")
_G.TweenService = _G.game:GetService("TweenService")
_G.RunService = _G.game:GetService("RunService")

_G.syn = {
    request = function(options)
        old_pr("syn.request(" .. serialize(options) .. ")")
        return {Success = true, Body = "{}", StatusCode = 200}
    end
}
_G.request = _G.syn.request
end

local function pretty(v)
    if type(v) == "table" then
        return tostring(v)
    elseif type(v) == "function" then
        return tostring(v)
    elseif type(v) == "string" then
        return string.format("%q", v)
    else
        return tostring(v)
    end
end

local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

local function base64_decode(data)
    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

local function deobf(code)
    local strings = {}
    for encoded in code:gmatch("'([^']+)'") do
        pcall(function()
            local decoded = base64_decode(encoded:reverse()):gsub("\n","")
            table.insert(strings, decoded)
        end)
    end
    if #strings == 2 then
        local func = strings[1]
        local arg  = strings[2]

        return string.format('%s(%q)', func, arg)
    end
end

spawn = spawn or function(fn) local co = coroutine.create(function() pcall(fn) end) coroutine.resume(co) end
task = task or {}
task.spawn = task.spawn or spawn
task.wait = function() return 0 end
task.defer = function(fn) spawn(fn) end
task.delay = function(d, fn) spawn(fn) end
wait = function() return 0 end

if old_prf then
    function error(f)
        old_pr(f)
    end
    function warn(f)
        old_pr(f)
    end
end

-- hook

local old_fenv = getfenv
local old_concat = table.concat
local old_pairs = pairs

local concatQueue = {}

if getfenvhook then
    local seen = {}
    
    local function safe(v)
        local t = typeof(v)
        if t == "string" then
            return string.format("%q", v)
        elseif t == "Instance" then
            return v:GetFullName()
        else
            return tostring(v)
        end
    end
    
    getfenv = function(level)
        local env = old_fenv(level)

        return setmetatable({}, {
            __index = function(_, key)
                if type(key) == "string" and not seen[key] then
                    seen[key] = true
                end
                
                local val = env[key]
                
                if type(val) == "function" then
                    return function(...)
                        local args = {...}
                        local out = {}
                        for i = 1, #args do
                            out[i] = safe(args[i])
                        end
                        
                        local re = tostring(
                            key .. "(" .. table.concat(out, ", ") .. ")", "\n"
                        )
                        
                        if type(re) == "string" then
                            old_pr(re, "\n")
                            return function() end
                        end
                        
                        return val(...)
                    end
                end
                return val
            end
        })
    end
end

if concathook then
    table.concat = function(tbl, sep, i)
        sep = sep or ""
        i = i or 1
        local result = old_concat(tbl, sep, i)
        table.insert(concatQueue, result)
        if #concatQueue >= 2 then
            local a = concatQueue[#concatQueue - 1]
            local b = concatQueue[#concatQueue]
            if a == "Text" then
                local m = a .. " = " .. string.format("%q", b)
                old_pr(m)
                return m
            end
        end
        if result == "ReplicatedStorage" then result = 'local v1 = game:GetService("ReplicatedStorage")' end
        if result == "HttpService" then result = 'local v2 = game:GetService("HttpService")' end
        if result == "Lighting" then result = 'local v3 = game:GetService("Lighting")' end
        if result == "RunService" then result = 'local v4 = game:GetService("RunService")' end
        if result == "MarketplaceService" then result = 'local v5 = game:GetService("MarketplaceService")' end
        if result == "CollectionService" then result = 'local v6 = game:GetService("CollectionService")' end
        if result == "Players" then result = 'local v7 = game:GetService("Players")' end
        if result == "Workspace" then result = 'local v8 = game:GetService("Workspace")' end
        if result == "TeleportService" then result = 'local v9 = game:GetService("TeleportService")' end
        if result == "GetMouse" then result = 'local v9 = v7.LocalPlayer:GetMouse()' end
        if result == "PlayerGui" then result = 'local v10 = v7.LocalPlayer.PlayerGui' end
        if result == "CharacterAdded" then result = 'v7.LocalPlayer.CharacterAdded' end
        if result == "Died" then result = 'v7.LocalPlayer.Character.Humanoid.Died' end
        if result == "HumanoidRootPart" then result = 'v7.LocalPlayer.Character:FindFirstChild("HumanoidRootPart")' end
        if result == "Humanoid" then result = 'v7.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")' end
        if result == "Character" then result = 'v7.LocalPlayer.Character' end
        if result == "LocalPlayer" then result = 'v7.LocalPlayer' end
        if result == "PlaceId" then result = 'game.PlaceId' end
        if result == "IsLoaded" then result = 'game.IsLoaded' end
        if result == "spawn" then result = 'spawn(function()' end
        if result == "CurrentCamera" then result = 'local v10 = workspace.CurrentCamera' end
        if result == "ScreenGui" then result = 'local v11 = Instance.new("ScreenGui")' end
        if result == "Frame" then result = 'local v12 = Instance.new("Frame")' end
        if result == "TextLabel" then result = 'local v13 = Instance.new("TextLabel")' end
        if result == "TextButton" then result = 'local v14 = Instance.new("TextButton")' end
        if result == "Custom" then result = 'Enum.CameraType.Custom' end
        if result == "Color" then result = 'Color3.new' end
        if result == "CFrame" then result = 'CFrame.new' end
        if result == "Vector3" then result = 'Vector3.new' end
        if result == "MouseButton1Click" then result = 'v14.MouseButton1Click:Connect(function()' end
        old_pr(tostring(result))
        return result
    end
end

if loadshook then
    loadstring = function(code)
        old_pr(tostring(code))
        return function() end
    end
end

if pairshook then
    pairs = function(t)
        local iter, tbl, key = old_pairs(t)
          
        return function(_, k)
            local v
            k, v = iter(tbl, k)
            if k ~= nil then
                old_pr(string.format(
                    "for i,v in pairs(%s) do\nprint(%s,%s)\nend\n",
                    pretty(tbl),
                    pretty(k),
                    pretty(v),
                    pretty(v)
                ))
            end
            return k, v
        end, tbl, key
    end
end

if log then
    print = function(...)
        local args, s = {...}, ""
        for i, v in ipairs(args) do s = s .. tostring(v) .. (i < #args and "\t" or "") end
        old_pr('print("' .. s .. '")')
    end
end

-- paste the script inside "script" and run
local script = [====[
local plr = game:GetService("Players").LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local hrp = char:WaitForChild("HumanoidRootPart")

local part = Instance.new("Part")
part.Parent = workspace
part.Shape = Enum.PartType.Ball
part.Color = Color3.fromRGB(255, 0, 0)
part.CFrame = hrp.CFrame
]====]

if constant then
    local res = const(deobf(script))
    old_pr(res)
else
    if decode then
        local decoded = deobf(script)
        old_pr(decoded)
    end
    script = addCompoundSupport(script)
    local func, err = load(script)
    if func then
        func()
    else
        old_pr("Error: " .. err)
    end
end
INFO