You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止Lua的loadstring被钩子劫持,保护其内部代码内容?

Lua中loadstring/load的反劫持方案

我在Lua中使用loadstring时,会被攻击者通过钩子代码劫持,导致传入的代码内容被窃取。攻击者的钩子会提前保存原生函数副本,替换_G表中的loadstring/load和tostring,伪造函数名称绕开tostring(loadstring)的验证,还能通过提前保存的writefile/print副本输出窃取的内容。

攻击者的钩子代码示例:

-- Save copies of orignal functions
local o_load = _G["load"]
local o_loadstring = _G["loadstring"]
local o_unpack = _G["unpack"]
local o_pairs = _G["pairs"]
local o_writefile = _G["writefile"]
local o_print = _G["print"]
local o_tostring = _G["tostring"]

-- Dynamic function names
local load_name = tostring(_G["load"])
local loadstring_name = tostring(_G["loadstring"])
local tostring_name = tostring(_G["tostring"])

-- For multiple instances of loadstring or load being used
local files_loadstring = 1
local files_load = 1

-- Hide function names
_G["tostring"] = function(...)
    local args = {...}  
    local arg = o_unpack(args)
    
    if arg == _G["tostring"] then
        return tostring_name
    end
    
    if arg == _G["loadstring"] then
        return loadstring_name
    end
    
    if arg == _G["load"] then
        return load_name
    end
    
    local ret = { o_tostring(o_unpack(args)) }
    local value = o_unpack(ret)
    
    return value
end

-- Hook loadstring
_G["loadstring"] = function(...)
    local args = {...}
    o_print("loadstring called")
    
    local str = ""
    for k, v in o_pairs(args) do
        str = str .. o_tostring(v) .. " "
    end

    o_writefile("hook_loadstring"..o_tostring(files_loadstring)..".lua", str)
    o_print("file written to hook_loadstring"..o_tostring(files_loadstring)..".lua")
    files_loadstring = files_loadstring +1
    
    local ret = { o_loadstring(o_unpack(args)) }
    str = ""
    for k, v in o_pairs(ret) do
        str = str .. o_tostring(v) .. " "
    end
    
    return o_unpack(ret)
end

-- Hook load
_G["load"] = function(...)
    local args = {...}
    o_print("load called")
    
    local str = ""
    for k, v in o_pairs(args) do
        str = str .. o_tostring(v) .. " "
    end
    
    o_writefile("hook_load"..o_tostring(files_load)..".lua", str)
    o_print("file written to hook_load"..o_tostring(files_load)..".lua")
    files_load = files_load +1
    
    local ret = { o_load(o_unpack(args)) }
    str = ""
    for k, v in o_pairs(ret) do
        str = str .. o_tostring(v) .. " "
    end
    
    return o_unpack(ret)
end

-- bellow is the loadstring function with a lua and all it's content is getting hooked and writen in a file
loadstring("local a = 1;")()

以下是可行的反劫持方案:

1. 提前缓存原生函数到局部变量(优先级最高)

如果你的代码能在攻击者钩子之前执行,直接将原生load/loadstring存到局部变量中——局部变量不受_G表污染的影响:

-- 必须放在所有代码(包括攻击者钩子)最开头执行
local native_load = load
local native_loadstring = loadstring

-- 后续执行动态代码时直接使用局部变量
native_loadstring("local a = 1;")()

2. 从debug库获取原生函数(环境允许时使用)

Lua的debug库可以访问虚拟机内部的原生函数,绕过_G的污染:

local function get_native_loaders()
    -- 通过匿名函数获取原始全局环境
    local dummy = function() end
    local raw_env = debug.getupvalue(dummy, 1)
    return raw_env.load, raw_env.loadstring
end

-- 获取原生函数
local native_load, native_loadstring = get_native_loaders()
-- 使用原生函数执行代码
native_loadstring("local a = 1;")()

注意:如果debug库也被攻击者劫持,此方法失效。

3. 加密动态代码内容(退而求其次的防护)

即使无法阻止劫持,也可以对要执行的代码进行加密,让攻击者只能拿到加密后的内容:

-- 示例:简单异或加密(实际使用更安全的加密算法)
local function xor_cipher(str, key)
    local result = ""
    local key_len = #key
    for i = 1, #str do
        local byte = string.byte(str, i) ~ string.byte(key, (i-1)%key_len + 1)
        result = result .. string.char(byte)
    end
    return result
end

-- 提前加密代码
local encrypted_code = xor_cipher("local a = 1;", "your_secret_key")

-- 执行时解密再加载
loadstring(xor_cipher(encrypted_code, "your_secret_key"))()

4. 检测函数是否为原生C函数

原生load/loadstring是C实现的,而攻击者的钩子是Lua函数,可以通过debug.getinfo的what字段区分:

local function is_native_function(func)
    if type(func) ~= "function" then return false end
    local info = debug.getinfo(func, "S")
    return info and info.what == "C"
end

-- 验证当前loadstring是否被篡改
if not is_native_function(loadstring) then
    error("loadstring has been hooked, aborting!")
end

注意:若攻击者篡改了debug.getinfo,此方法失效。

内容的提问来源于stack exchange,提问作者Alvee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:01:04