Sunday, March 20, 2022

Warmane: scripts sent by Sentinel

During login and while you play, Sentinel sends some Lua code to your client using the addon message channel. It's not clear to me how this code is executed once it arrives to the client or why is it being sent using addon messages. Some of these scripts add UI features while others try to catch cheaters. 

I am publishing this as a technical curiosity more than anything else. Excuse the format, I know how much blogspot sucks for posting snippets.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if(OriginalClearTarget==nil)then
    OriginalClearTarget=ClearTarget
    ClearTarget=function()
        if(issecure())then
            OriginalClearTarget()
        else
            RegisteredFrames={GetFramesRegisteredForEvent("MACRO_ACTION_FORBIDDEN")}
            RegisteredFramesCount=getn(RegisteredFrames)
            for i=1,RegisteredFramesCount do
                RegisteredFrames[i]:GetScript("OnEvent")(RegisteredFrames[i],"MACRO_ACTION_FORBIDDEN","ClearTarget()")
            end
        end
    end
end

Relays the event MACRO_ACTION_FORBIDDEN to all subscribed frames whenever the protected function ClearTarget() is used by tainted code.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

local LogoutFrame=CreateFrame("Frame")
LogoutFrame:RegisterEvent("PLAYER_LOGOUT")
LogoutFrame:SetScript("OnEvent",function()
    SendAddonMessage('(redacted)','(redacted)','WHISPER','(redacted)')
end)

Sends back a message whenever the player logs out or reloads the interface. 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if not (UnitPopupButtons["PVP_REPORT_AFK"]) then
    return
end
local soloqueueFrame=CreateFrame("Frame")
soloqueueFrame:RegisterEvent("UPDATE_BATTLEFIELD_STATUS")
soloqueueFrame:SetScript("OnEvent",function()
    local a,b,c,d,e=GetBattlefieldStatus(1)
    if (e==0xFF) then
        SOLOQUEUE_AVOID_TEAMMATE="Avoid as Teammate"
        UnitPopupButtons["PVP_REPORT_AFK"].text=SOLOQUEUE_AVOID_TEAMMATE
    else
        UnitPopupButtons["PVP_REPORT_AFK"].text=PVP_REPORT_AFK
    end
end)
soloqueueFrame:GetScript('OnEvent')()

This is the code that adds the "Avoid as Teammate" option in soloq games.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

local XPRate,XPRates,dropDown=7,{0.5,1,3,5,7},CreateFrame("Frame","XPRM",MainMenuExpBar,"UIDropDownMenuTemplate")
UIDropDownMenu_Initialize(dropDown,XPRMDD,"MENU")
MainMenuExpBar:SetScript("OnMouseDown",function(self,button)
    if button=="RightButton" then
        ToggleDropDownMenu(1,nil,dropDown,"cursor",3,-3)
    end
end)
UIDropDownMenu_Initialize(dropDown,function(self,level,menuList)
    local info=UIDropDownMenu_CreateInfo()
    local title=info
    title.text=EXPERIENCE_COLON
    title.isTitle=1
    UIDropDownMenu_AddButton(title,level)
    info=UIDropDownMenu_CreateInfo()
    info.func=self.SetValue
    for i=1,#XPRates do
        local currate=XPRates[i]
        info.text,info.arg1,info.checked="x"..currate,currate,currate == XPRate
        UIDropDownMenu_AddButton(info,level)
    end
end)
function dropDown:SetValue(xp)
    XPRate=xp
    SendAddonMessage('(redacted)',xp,'WHISPER','(redacted)')
    CloseDropDownMenus()
end

This is the dropdown that allows you to choose an experience multiplier by right-clicking the experience bar.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MERCENARYMODE_ENABLED=false

function ToggleMercenaryMode(a,t)
    MERCENARYMODE_ENABLED=t
    SendAddonMessage('(redacted)',t and 1 or 0,'WHISPER','(redacted)')
    CloseDropDownMenus()
end

local function b(a,c,a)
    if c==2 then
    local d=UIDropDownMenu_CreateInfo()
    d.text="On"
    d.checked=MERCENARYMODE_ENABLED
    d.func=ToggleMercenaryMode
    d.arg1=true
    UIDropDownMenu_AddButton(d,c)
    local d=UIDropDownMenu_CreateInfo()
    d.text="Off"
    d.checked=not MERCENARYMODE_ENABLED
    d.func=ToggleMercenaryMode
    d.arg1=false
    UIDropDownMenu_AddButton(d,c)
    return
end

MiniMapBattlefieldDropDown_Initialize()
    local e=0
    for f=1,MAX_BATTLEFIELD_QUEUES do
        s,a,a,a,a,t,r=GetBattlefieldStatus(f)
        if s=="queued" and t==0 and r~=1 then
            e=1
        end
    end
    
    if e==1 then
        local d=UIDropDownMenu_CreateInfo()
        d.isTitle=1
        d.notCheckable=1
        UIDropDownMenu_AddButton(d)
        local d=UIDropDownMenu_CreateInfo()
        d.isTitle=1
        d.notCheckable=1
        d.tooltipWhileDisabled=1
        d.tooltipOnButton=1
        d.tooltipTitle="Mercenary mode allows you to enter Battlegrounds as a member of the opposite faction for improved queue times."
        d.text="Queue Options"
        UIDropDownMenu_AddButton(d)
        local d=UIDropDownMenu_CreateInfo()
        d.text="Mercenary Mode"
        d.notCheckable=1
        d.hasArrow=1
        UIDropDownMenu_AddButton(d)
    end
end

local function g()
    UIDropDownMenu_Initialize(MiniMapBattlefieldDropDown,b,"MENU")
end

g()

local h=CreateFrame("Frame")
h:RegisterEvent("PLAYER_ENTERING_WORLD")
h:SetScript("OnEvent",OnEvent)

This is the code that allows you to toggle mercenary mode in the battleground button dropdown.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if(issecurevariable("ClearTarget")) then
    SendAddonMessage('(redacted)','(redacted 1)','WHISPER','(redacted)')
else
    SendAddonMessage('(redacted)','(redacted 2)','WHISPER','(redacted)')
end

This one is sent periodically. It will send different addon messages depending on whether you interfered with the modifications done by the first script or not.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if(something)then
    local function IsLowercase(input)
        -- returns 97 if all chars in input are lowercase else 65
        return input:lower()==input and ('a'):byte() or ('A'):byte()
        end

    local function Unscramble(str,inputKey)
        return (str:gsub('%a',function(input)
            local caseKey=IsLowercase(input)
            return string.char(((input:byte()-caseKey+inputKey)%(28+10-12))+caseKey)
        end))
    end

    local CVarName="(redacted)"
    local MyInputKey=7
    local GetCVar_fun="(redacted)"
    local strlen_fun="(redacted)"
    local SendAddonMessage_fun="(redacted)"
    local Channel=Unscramble("(redacted)",-(MyInputKey*3))
    CVarName=Unscramble(CVarName,(MyInputKey+3))
    SendAddonMessage('(redacted)',_G[Unscramble(strlen_fun,12)](_G[Unscramble(GetCVar_fun,(MyInputKey))](CVarName)),'WHISPER','(redacted)')
end

This checks if there is a cvar called "SCRIPT_HANDLER_LOAD" and if so it sends its length back to the server as an addon message.  

There's a very similar one that checks for the existence of a function called "unlock". If detected an addon message is sent also.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if(FirstFrame)then
    FirstFrame:UnregisterAllEvents()
    FirstFrame:SetScript("OnUpdate",nil)
    FirstFrame.(redacted)=0
    FirstFrame.(redacted)=0
    FirstFrame=nil
end
if(OriginalClearTarget)then
    ClearTarget=OriginalClearTarget
    OriginalClearTarget=nil
end

Not sure about this one at all. Seems to undo some of the previous things, although I see no reference to FirstFrame in any of the other scripts (perhaps an older script that is no longer in use?).