Modcraft - The community dedicated to quality WoW modding!

Wrath of the Lich King Modding => Miscellaneous => Topic started by: Grymskvll on August 03, 2016, 08:27:59 pm

Title: [LUA] Modifying secure variables without taint
Post by: Grymskvll on August 03, 2016, 08:27:59 pm
If there's a better solution, or if there are any improvements to be made to this, please let me know.

I was working on a custom secondary class resource system (demo here: https://www.youtube.com/watch?v=qHSFOsNAs_A (https://www.youtube.com/watch?v=qHSFOsNAs_A" onclick="window.open(this.href);return false;)) and ran into the phenomenon of interface taint (more info here: http://wowwiki.wikia.com/wiki/Secure_Ex ... d_Tainting (http://wowwiki.wikia.com/wiki/Secure_Execution_and_Tainting" onclick="window.open(this.href);return false;)).

From what I understand, normally when you're modding the interface for addons, you don't necessarily need to directly change the return value of API functions. However, I needed to change the return value of certain secure functions based on custom conditions (like checking custom resource values to see if a spell is usable).

The problem is that directly changing secure functions will taint them, causing interface errors (or at least it causes ugly notifications to be printed in the chat window). The good news is there's apparently a way to modify secure variables (including functions).

Here's how:


1) Requires a patched Wow.exe to load modified interface files (see http://www.ownedcore.com/forums/world-o ... mover.html (http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/501200-repost-sig-md5-protection-remover.html" onclick="window.open(this.href);return false;)).

2) Find the most up-to-date FrameXML.toc (GlueXML.toc should work too) in your client. For the standard WotLK 3.3.5 version, it should be in [your-WoW-folder]Data[locale]patch-[locale]-3.MPQ
Full path:
InterfaceFrameXMLFrameXML.toc

3) Extract FrameXML.toc to [your-WoW-folder]InterfaceFrameXML, or make a new MPQ for it. Either should work.

4) Open it up in any text editor and add this new line at the bottom (the path and filename are arbitrary, change it if you feel like it):
Code: [Select]
..CustomSecureVarsCustomSecureVars.lua
5) Create a new folder in [your-WoW-folder]Interface, name it CustomSecureVars. In the new folder, create a text file called CustomSecureVars.lua. This file will hold all the new secure variables you need. Feel free to edit secure functions all you want, they'll still be secure so long as you don't taint them by accessing any tainted variables.

Top tips for keeping variables secure:


If you want to call a (potentially) tainted function, use the "securecall" API function (see http://wowwiki.wikia.com/wiki/API_securecall (http://wowwiki.wikia.com/wiki/API_securecall" onclick="window.open(this.href);return false;)).

If you need to provide a tainted variable as an argument from a modified secure function, provide it as a string and have the called function resolve the string to a global variable. That way the secure function stays secure, and only the called function needs to deal with taint.

For debugging, you can check individual variables with "issecurevariable" (see http://wowwiki.wikia.com/wiki/API_issecurevariable (http://wowwiki.wikia.com/wiki/API_issecurevariable" onclick="window.open(this.href);return false;))

You can also enable taint logging (http://wow.gamepedia.com/CVar_taintLog (http://wow.gamepedia.com/CVar_taintLog" onclick="window.open(this.href);return false;)). Any time your chat window shows "Interface action failed because of an AddOn", it'll write a report in WoWLogstaint.log. Remember to delete your taint.log in between tests for convenience! And remember to turn off logging after your tests!

I don't always get an interface error when accessing a tainted variable from a secure function, I'm not entirely sure how it works. Specific lines of code seem consistent, but different modifications don't always cause an error.

Example:


Lets say you have a tainted variable:
Code: [Select]
TaintedVar = "hi I'm a tainted variable"
You can't keep it secure because it's managed by an AIO script or addon. You also want to check TaintedVar inside the secure GetShapeshiftFormInfo API function, so you hook it:
Code: [Select]
local origGetShapeshiftFormInfo = GetShapeshiftFormInfo;
GetShapeshiftFormInfo = function(...)
local texture, name, isActive, isCastable = origGetShapeshiftFormInfo(...)

if (TaintedVar) then print(TaintedVar) end -- oops, we're accessing a tainted variable!

return texture, name, isActive, isCastable
end

Testing this on a character with a shapeshift/stances bar, I get "Interface action failed because of an AddOn" during combat. The solution is to make a new function that's allowed to get tainted (because it's not used by the official UI), the new function will return the tainted value we want to check. We'll call the new function using securecall, providing the name of the tainted value we want to check.

Code: [Select]
local function GetTaintedVar(varname)
-- _G[variable name] retrieves a global variable
return _G[varname]
end

local origGetShapeshiftFormInfo = GetShapeshiftFormInfo;
GetShapeshiftFormInfo = function(...)
local texture, name, isActive, isCastable = origGetShapeshiftFormInfo(...)

-- Even if GetTaintedVar is tainted, secure status is restored after the call
local SecureVar = securecall(GetTaintedVar, "TaintedVar")
if (SecureVar) then print(SecureVar) end

return texture, name, isActive, isCastable
end

The key here is that the function that has to stay secure (GetShapeshiftFormInfo) doesn't access any tainted variables, except the GetTaintedVar function, which it provides as an argument for securecall, but thanks to the magic of securecall, secure status is restored after the call to securecall. TaintedVar is just provided as a variable name string, and then checked by the function that we're allowing to get tainted. It seems that the function being called by securecall is allowed to be tainted, but not any arguments provided along with it, thus the need to provide the name of the variable as a string.