-
-
Notifications
You must be signed in to change notification settings - Fork 2
Universal Method Finder
Or Shortly UMF
local globalenv = getgenv and getgenv() or _G or shared
local globalcontainer = globalenv.globalcontainer
if not globalcontainer then
globalcontainer = {}
globalenv.globalcontainer = globalcontainer
end
local genvs = { _G, shared } -- Could become an issue if the latest function it gets is unaccessible by client
if getgenv then
table.insert(genvs, getgenv())
end
-- if getrenv then -- Add this if you wish to search through game's env (normal Scripts)
-- table.insert(genvs, 1, getrenv())
-- end
-- if debug and debug.getregistry then
-- table.insert(genvs, 1, debug.getregistry()._LOADED) -- Includes things like string / table / math library tables etc. Basically everything roblox offers, but due to being too huge you should specify library tables by attaching ".string" or to LOADED; adding "_G" to this will link a globals table. That way the finder will have to scan way less.
-- end
local calllimit = 0
do
local function determineCalllimit()
calllimit = calllimit + 1
determineCalllimit()
end
pcall(determineCalllimit)
end
local depth, printresults, hardlimit, query, antioverflow -- prevents infinite / cross-reference
local function recurseEnv(env, envname)
if globalcontainer == env then
return
end
if antioverflow[env] then
return
end
antioverflow[env] = true
depth = depth + 1
for name, val in next, env do
local Type = type(val)
if Type == "table" then
if depth < hardlimit then
recurseEnv(val, name)
else
-- warn("almost stack overflow")
end
elseif Type == "function" then -- This optimizes the speeds but if someone manages (??) to fool this then rip
name = string.lower(tostring(name))
local matched
for methodname, pattern in next, query do
if pattern(name, envname) then
globalcontainer[methodname] = val
if not matched then
matched = {}
end
table.insert(matched, methodname)
if printresults then
print(methodname, name)
end
end
end
if matched then
for _, methodname in next, matched do
query[methodname] = nil
end
end
end
end
depth = depth - 1
end
local function finder(Query: table, ForceSearch: boolean, CustomCallLimit: number, PrintResults: boolean): nil
antioverflow = {}
query = {}
do -- Load patterns
local function Find(String, Pattern)
return string.find(String, Pattern, nil, true)
end
for methodname, pattern in next, Query do
if not globalcontainer[methodname] or ForceSearch then
if not Find(pattern, "return") then
pattern = "return " .. pattern
end
query[methodname] = loadstring(pattern)
end
end
end
depth = 0
printresults = PrintResults
hardlimit = CustomCallLimit or calllimit
recurseEnv(genvs)
hardlimit = nil
depth = nil
printresults = nil
antioverflow = nil
query = nil
end
return finder, globalcontainer
Query being a table type (dictionary in this case) uses values (strings) to match names of functions inside different environments. It performs "loadstring" on the pattern and passes the name of the function as an argument to the loadstring. If name of the function matches the pattern (logical statement) you provided as string then it will write said function under the name (key) you provided inside any of these 3 environments (depending on which one is available): getgenv, _G, shared.
Structure in short:
Query (table)
Key - a name that you want the function to have inside global environment (either getgenv, _G or shared) once it is found.
Value (string) - a logical statement that will be put inside loadstring along with name of the current function being checked.
ForceSearch (boolean) - forces search of already found methods therefore overwriting them with freshly found methods(if found that is).
CustomCallLimit (number) must be less than the current depth of the script (or less than 20,000 - 3 in most cases unless function nesting and/or recursion takes place, pcalls, xpcalls, loadstrings, coroutines count too!)
PrintResults (boolean) - prints matched results.
So let's say you want to find methods for gethiddenproperty
& queue_on_teleport
, to do so you are supposed to figure out the logical statements that match only the methods you need. It is mostly trial and error to figure out, but general advice is:
- Split / Spread out your string patterns as much as possible (for example, instead of
(...):find("gethiddenproperty", nil, true)
you are supposed to do(...):find("get", nil, true) and (...):find("hid", nil, true) and (...):find("prop", nil, true)
). Otherwise you are destroying the point of this script (unless you really need to match "gethiddenproperty" string exactly for some reason. In case you are wondering(...)
is basically the name of the function that's being currently checked. - Make sure the strings you provide for "find" method are all lowercase.
- Make sure the strings you provide are all a one liner so that they actually act as a string (no newlines). This is mostly a concern if you are using a complex code variant for matching like for the
queue_on_teleport
. To achieve that you could use a minifier for Lua or LuaU, or if you prefer a Plugin for Visual Studio Code which supports both libraries listed previously. They will allow you to minify your code into a one-liner format which is suitable for strings. - Function names vary per executor which is why this script was made. For that matter you are supposed to visit documentations of executors that you want to support. Another good source of names are other compatibility scripts on sites like v3rmillion, as they usually work by spamming all names in fashion of
local s = syn and syn.write_clipboard or write_clipboard or clipboard and clipboard.set or copystring or setclipboard or toClipboard or toclipboard or set_clipboard or Clipboard and Clipboard.set
. For example scripts like these include a lot of names IrisBetterCompat, Infinite Yield or Hydroxide. Another method is to search Github-wide for your functions' names and see what else people use. - After figuring out the part above, you need to find the similarity between various names of same method that you want to find. And then convert it to a logical statement that would match ONLY that method successfully.
- Sometimes "debug" library and other Roblox libraries might have the methods you need (usually executors add them back if Roblox removed something). You are supposed to set them before using the finder. This way they will be used in case method finder does not find anything better! YOU MUST SET ForceSearch TO TRUE or ASSIGN METHODS A NEW NAME WHEN PROMPTING FINDER IF YOU ARE PRE-DEFINING METHODS (example in code below) (E.G. globalcontainer.getrawtable = debug.getmetatable)
- Environment names are also passed into your logical statement strings as second argument of the
...
Tuple. And can be accessed aslocal arg2 = ({ ... })[2]
. Take a look at thehash
logical statement string which matcheshash
function of thecrypt
library (environment). Be careful when matching environments as their names can have types other than string so make an if check for it or use tostring(). - Enable PrintResults (pass "true" as second parameter to finder) when testing to see what names it matches.
- In the case of
queue_on_teleport
, you can see how complex the logical statement is. It is because KRNL has a method calledrun_teleport_queue_scripts
which we do NOT want to match with our finder because that function does not do what we are searching for (queue_on_teleport
in this case). When using this variant **make sure there is at least onereturn
that returns the match result in the code you provide otherwise the finder will put areturn
at the start of the logical statement code string which will likely break your code. The non-minifed version of the code for matching (example, can be optimized)queue_on_teleport
:
local arg1 = ({ ... })[1]
local result = (arg1):find("queue", nil, true) and (arg1):find("tele", nil, true)
if result and KRNL_LOADED then
print("Script is being ran on KRNL, performing an extra check")
result = not ((arg1):find("run", nil, true) or (arg1):find("script", nil, true))
end
return result
- Complex variant of code, unless you truly believe there is no other way to match whatever you need
- Methods that have not been found / defined yet. Also, methods that belong exclusively to some exploit like
getsynasset
(notice thesyn
in the name), because it destroys the point of using this finder in the first place. Unless it's something likeKRNL_LOADED
to only detect KRNL.
-
...
is a vararg meaning it is a Tuple. It can be packet into an array using {...} (faster) or table.pack(...) and then have it's arguments accessed as packed[1], packed[2] and so on. The reason we use it is that it's the only way to avoid turning the logical statement string into a complex conditional (logical statement) function. The reason we don't need to pack it is because of how tuples work, when you perform actions on ... without packing it then the actions will be only performed on the first argument inside the tuple (if present). -
:find("lowercase_string", nil, true)
looks like that is because of the optimization that 3rd parameter (plain) brings. It basically disables the "magic" pattern-matching characters inside of the string and the string is treated as-is. If you are familiar with patterns like "%d" for matching any digit (number) you'll know what this means. More information at find documentation and String Pattern Reference HOWEVER, if you do wish to match strings with the use of those patterns you must make you finds like these:find("lowercase_pattern_string")
local FileName = "UniversalMethodFinder"
local finder, globalcontainer =
loadstring(game:HttpGet("https://raw.githubusercontent.com/luau/SomeHub/main/" .. FileName .. ".luau", true), FileName)()
-- globalcontainer can also be indexed using "local container = (getgenv and getgenv() or _G or shared).globalcontainer"
-- Below is the example how sometimes "debug" library and other Roblox libraries might have the methods you need (usually executors add them back if Roblox removed something). You are supposed to set them before running this script aka before the loadstring. This way they will be used in case method finder does not find anything better! Pretty good isn't it.
-- If you do use the Roblox libraries then you need to set to true the ForceSearch boolean parameter or you can assign methods a new name when using finder. If you do not wish to enable the ForceSearch parameter then you must assign them after you've used the finder and if nothing was found then assign the values from the "debug" library in this case - example for this is at the bottom of the code. If you pick this variant then you must remove the similar lines of code at the top as having both is pointless.
if debug and debug.getmetatable then
globalcontainer.getrawtable = debug.getmetatable
end
finder({
getrawtable = '(...):find("get", nil, true) and (...):find("raw", nil, true) and (...):find("tab", nil, true)',
gethiddenprop = '(...):find("^get") and (...):find("hid", nil, true) and (...):find("prop", nil, true)', -- An example of logical statement string that makes use of the string pattern syntax (:find("^get"), `^` being an Anchor and declaring the start of the string meaning get must be at the very start to match)
queue_on_teleport = 'local arg1 = ({ ... })[1] local result = (arg1):find("queue", nil, true) and (arg1):find("tele", nil, true) if result and KRNL_LOADED then print("Script is being ran on KRNL, performing an extra check") result = not ((arg1):find("run", nil, true) or (arg1):find("script", nil, true)) end return result', -- An example of a complex logical statement that requires more than one of code in order to achieve the goal result which otherwise might not be possible. Usually, you wouldn't need this unless you can't fit your logical statement into one "if" line and require extra control of the matching process
hash = 'local args = { ... } local arg1 = args[1] local arg2 = tostring(args[2]) local result = (arg2):find("crypt", nil, true) and (arg1):find("hash", nil, true) return result', -- A good example of matching by the environment name (`crypt` in this case). Be careful when matching environments as their names can have types other than string so make an if check for it or use tostring().
}, true, nil, true) -- ForceSearch = true, PrintResults = true, generally the second one is not needed if you don't need to find the methods again for some reason. It MUST BE SET TO TRUE if you are pre-defining a fallback from Roblox library like debug (e.g. debug.getmetatable), otherwise it will not try to find anything better because the method has already been defined.
-- If your executor supports getgenv() you could remove the part up until the dot. So only leave the function in that case.
-- WARNING: Sometimes methods might be NOT found and therefore nil so you should check if they exist before using them!
-- For people who don't want to use ForceSearch boolean parameter but still want to assign a fallback value in case finder didn't find anything. If you pick this variant then you must remove the similar lines of code at the top as having both is pointless.
if not globalcontainer.getrawtable and debug and debug.getmetatable then
globalcontainer.getrawtable = debug.getmetatable
end
local gm = globalcontainer.getrawtable(game)
globalcontainer.gethiddenprop(workspace.Terrain, "Decoration", false)
globalcontainer.queue_on_teleport('print("queue ran!")')
ALL DISCUSSIONS & QUESTIONS GO TO:
https://github.com/luau/SomeHub/discussions
Footer page
- text in red
+ text in green
! text in orange
# text in gray
@@ text in purple (and bold)@@
Test Siderbar