luamonkeypatchingcopy-protection

How can I prevent loadstring from getting hooked like this


Here I have a loadstring but attacker can just hook it and steal it's content like bellow. I want to make a anti hook so that it can be prevented.

-- 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;")()`

I tried using tostring(loadstring) to compare the function name to check if it's real or fake but attacker can just hook tostring as well and give original function name so my anti hook won't work at all. Also I can't do writefile = nil or print = nil so attacker can't print or write the file since he saved a copy of these functions on top of the file so his code will work always. How can I prevent my loadstring from getting hooked like this. Please help.
I just want my content inside loadstring not to be stolen.


Solution

  • loadstring being hooked implies that the attacker's code runs before your code and in the same environment. This implies that all functions of the global environment may be hooked - from loadstring over tostring and including the debug library. Thus, the only thing you may rely on (and even that is uncertain, depending on which software is interpreting your Lua) are "hard-wired" (e.g. syntactical) Lua language features. You can take a look at the complete syntax of Lua. Notice that all these are basic constructs that won't help you at all; everything advanced in Lua uses function calls and the global environment, which may be hooked.

    Whatever loads these scripts would need to be patched to either (1) load everything in a separate, clean environment (2) protect the global environment or at least a few selected functions, but even then the "loader" could be tampered with to remove these "protections".

    In conclusion, you can't reliably prevent or detect loadstring being hooked. If you are providing someone with code for them to run, that code inevitably has to run at some point, and at that point they will be able to "steal" it. The "best" you can do is obfuscate your code so that there is little benefit from stealing it.

    Bottom line: As long as your software runs "on-premise" (on their machines) rather than "as a service" (on your machines), they will always be able to steal and analyze your code; all you can do is make it harder e.g. through obfuscation or checks (which may be circumvented however).