> ## Documentation Index
> Fetch the complete documentation index at: https://starcryptdocs.starsolenterprise.com/lua-docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Environment Spoofing in Lua/Luau

> Replace and proxy the Lua global environment to intercept, redirect, or falsify access to globals, functions, and tables that an analyst might use to inspect your script.

Environment spoofing uses Lua's environment mechanisms to replace the global environment with a proxy table that controls what your script sees and what an analyst can intercept. In Lua 5.1, you use `setfenv`. In Lua 5.2 and later, you use the `_ENV` upvalue. In Luau, the global environment is static and managed by the host, so you must use module-level closures and upvalues to achieve similar isolation.

This page explains how Lua environments work, how to build proxy environments, how to sandbox analyst tools, and how to trap global mutations.

## How Lua Environments Work

Lua's environment system determines where global variable lookups resolve. Understanding the differences between versions is essential before you build a spoofing layer.

### Lua 5.1: setfenv

In Lua 5.1, every function has an associated environment table. Globals inside that function are looked up in this table. The `setfenv(fn, table)` function replaces a function's environment:

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local function my_sensitive_function()
    print("This should not be intercepted")
end

local proxy = {}
setmetatable(proxy, { __index = _G })
proxy.print = function(...) end -- suppress output

setfenv(my_sensitive_function, proxy)
my_sensitive_function() -- print is suppressed
```

`setfenv` affects the target function and any functions it calls, unless they have their own environments set.

### Lua 5.2+: \_ENV

In Lua 5.2 and later, `_ENV` is an upvalue in every chunk. Assigning to `_ENV` changes the environment for that chunk. You can also run code in a custom environment using `load`:

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local custom_env = setmetatable({}, { __index = _G })
local fn = load("print('hello')", "chunk", "t", custom_env)
fn() -- uses custom_env
```

The `_ENV` mechanism is cleaner than `setfenv` because it is lexically scoped and visible in the source.

### Luau: Static Global Environment

Luau does not support `setfenv`, and `_ENV` is not a writable upvalue. The global environment is static and managed by the host (for example, Roblox). You cannot replace the global environment in Luau. Instead, isolate sensitive code inside modules that capture only the upvalues you intend to expose, and avoid global lookups entirely.

## Creating a Proxy Environment (Lua 5.1)

A proxy environment is a table with `__index` and `__newindex` metamethods that delegate to `_G` by default, but allow you to override specific globals selectively.

### Basic Proxy Setup

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local function create_proxy_env()
    local proxy = {}
    local mt = {
        __index = function(t, k)
            return _G[k]
        end,
        __newindex = function(t, k, v)
            _G[k] = v
        end,
    }
    setmetatable(proxy, mt)
    return proxy
end
```

This proxy behaves exactly like `_G` unless you override specific keys.

### Selective Override

Override sensitive functions to prevent an analyst from using them:

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local proxy = create_proxy_env()

-- Suppress debug library entirely
proxy.debug = {}

-- Block dynamic code loading
proxy.load = function() error("load is disabled") end
proxy.loadstring = function() error("loadstring is disabled") end

-- Prevent module injection
proxy.require = function() error("require is disabled") end

-- Run sensitive code inside the proxy
setfenv(my_sensitive_function, proxy)
```

### Full Example

Here is a complete example that creates a proxy, overrides analyst tools, and runs code inside it:

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local function run_in_sandbox(fn)
    local proxy = {}
    setmetatable(proxy, {
        __index = function(_, k)
            if k == "debug" then
                return {} -- empty debug library
            elseif k == "load" or k == "loadstring" then
                return function() error("code loading disabled") end
            elseif k == "require" then
                return function() error("module loading disabled") end
            end
            return _G[k]
        end,
        __newindex = function(_, k, v)
            _G[k] = v
        end,
    })

    setfenv(fn, proxy)
    return fn()
end

local function sensitive_logic()
    -- This code cannot access the real debug library
    -- and cannot load external code
    local x = 42
    return x * 2
end

local result = run_in_sandbox(sensitive_logic)
```

## \_ENV Replacement (Lua 5.2+)

In Lua 5.2 and later, you can replace `_ENV` at the top of a chunk to redirect all global lookups through a proxy:

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local _ENV = setmetatable({}, {
    __index = function(_, k)
        if k == "debug" then
            return {}
        end
        return _G[k]
    end,
    __newindex = function(_, k, v)
        _G[k] = v
    end,
})

-- All global reads in this chunk now go through the proxy
local function secret_calculation()
    return math.random(1, 100)
end
```

Because `_ENV` is lexically scoped, only the chunk where you declare it is affected. Nested functions inherit the same `_ENV` unless they declare their own.

## Sandboxing the Analyst's Tools

The most effective use of environment spoofing is neutralizing the tools an analyst uses to inspect your script. Override these targets systematically:

| Target | Purpose | Override Strategy |
| - | - | - |
| `debug` | Inspection and hooking | Return empty table or nil |
| `load` / `loadstring` | Dynamic code execution | Error or return no-op |
| `require` | Module loading | Error or whitelist modules |
| `print` | Output interception | No-op or decoy output |
| `getmetatable` / `setmetatable` | Metatable inspection | Filter or restrict |

### Nullifying the Debug Library

The debug library is the analyst's most powerful tool. Neutralize it completely in the proxy:

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local fake_debug = {
    getinfo = function() return {} end,
    getlocal = function() return nil end,
    getupvalue = function() return nil end,
    sethook = function() end,
    gethook = function() return nil, "", 0 end,
}

proxy.debug = fake_debug
```

An analyst calling `debug.getinfo` will receive an empty table instead of real stack information.

## Global Mutation Trapping

Use `__newindex` on the environment proxy to detect when an analyst tries to inject or modify a global variable. This can catch attempts to hook functions via global replacement:

```lua theme={"theme":{"light":"ayu-dark","dark":"night-owl"}}
local function create_trapping_proxy()
    local proxy = {}
    local allowed_globals = {
        math = true,
        string = true,
        table = true,
    }

    setmetatable(proxy, {
        __index = _G,
        __newindex = function(t, k, v)
            if not allowed_globals[k] then
                -- Analyst is trying to inject or modify a global
                log_suspicious_activity(k, v)
                -- Optionally block the assignment
                -- rawset(t, k, v) -- or silently ignore
            else
                rawset(t, k, v)
            end
        end,
    })

    return proxy
end
```

If an analyst executes `print = my_hook`, the `__newindex` fires and you can log the attempt or take defensive action.

> [!NOTE]
>
> In Luau and Roblox, the global environment is managed by Roblox and `setfenv` is not available. You cannot replace the global environment in Luau. Instead, use module-level closures and upvalues to achieve similar isolation. Define all sensitive logic inside a module that captures only the functions and tables it needs as local upvalues, and never references globals.

> **Can environment spoofing prevent all hooking?**
>
> No. A sufficiently privileged debugger can bypass environment proxies by operating at the C level or through the C API. For example, `debug.sethook` in a separate coroutine, or a native debugger attached to the process, does not rely on the Lua global environment at all. Environment spoofing raises the bar for Lua-level analysts but must be combined with anti-debug checks and other runtime protections for defense in depth.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.