Jump to content
  • [Security] CastSpellByName


    nax
    • Product: WRobot General Type: Suggestion Status: Unconfirmed

    Hello @Droidz,

    I would like to propose an improvement to WRobot's spell casting functionality that enhances both evasion capabilities and reliability on servers that implement Lua-based detection mechanisms.

    Problem Statement

    Several private servers employ Lua hooks on the CastSpellByName function to detect and flag bot activity. The current implementation's reliance on this Lua function creates a detectable pattern that can be exploited for bot identification.

    Solution Overview

    I have developed two native methods that bypass the Lua layer entirely, thereby reducing the bot's detectable footprint:

    Method 1: Direct Spell ID Resolution

    The GetSpellIdByName method directly invokes the game's internal GetSpellIdByName function (0x00540200), retrieving both the spell ID and rank without requiring Lua interaction:

        public static int GetSpellIdByName(string spellName, out int rank)
        {
            rank = 0;
    
            try
            {
                uint GetSpellIdByNameAddr = 0x00540200;
    
                // Allocate space for spell name string
                var nameBytes = System.Text.Encoding.UTF8.GetBytes(spellName + "\0");
                uint nameSpace = wManager.Wow.Memory.WowMemory.AllocData.Get(nameBytes.Length);
    
                if (nameSpace <= 0)
                    return 0;
    
                // Allocate space for rank (int = 4 bytes)
                uint rankSpace = wManager.Wow.Memory.WowMemory.AllocData.Get(4);
    
                // Allocate space to store the return value (spell ID from eax)
                uint spellIdSpace = wManager.Wow.Memory.WowMemory.AllocData.Get(4);
    
                if (nameSpace <= 0 || rankSpace <= 0 || spellIdSpace <= 0)
                    return 0;
    
                // Write spell name to memory
                wManager.Wow.Memory.WowMemory.Memory.WriteBytes(nameSpace, nameBytes);
                wManager.Wow.Memory.WowMemory.Memory.WriteInt32(rankSpace, 0);
                wManager.Wow.Memory.WowMemory.Memory.WriteInt32(spellIdSpace, 0);
    
                var asm = new[]
                {
                    "push " + rankSpace,           // Push pointer to rank (2nd arg)
                    "push " + nameSpace,           // Push pointer to name (1st arg)
                    "call " + GetSpellIdByNameAddr,
                    "add esp, 8",
                
                    // Store the spell ID (eax) to spellIdSpace
                    "mov ecx, " + spellIdSpace,
                    "mov [ecx], eax",
    
                    "retn"
                };
    
                wManager.Wow.Memory.WowMemory.InjectAndExecute(asm);
    
                // Read back the spell ID and rank
                int spellId = wManager.Wow.Memory.WowMemory.Memory.ReadInt32(spellIdSpace);
                rank = wManager.Wow.Memory.WowMemory.Memory.ReadInt32(rankSpace);
    
                // Free allocated memory
                wManager.Wow.Memory.WowMemory.AllocData.Free(nameSpace);
                wManager.Wow.Memory.WowMemory.AllocData.Free(rankSpace);
                wManager.Wow.Memory.WowMemory.AllocData.Free(spellIdSpace);
    
                return spellId;
            }
            catch (System.Exception ex)
            {
                Logging.WriteError("Error getting spell ID by name: " + ex.Message);
                return 0;
            }
        }

    Method 2: Direct Spell Casting with GUID Support

    The CastSpell method invokes the native spell casting function (0x080DA40) directly, accepting a GUID parameter to eliminate the need for target selection: 

    public static void CastSpell(int spellid, ulong guid = 0)
    {
        try
        {
            uint CastSpell = 0x080DA40;
            uint guidLow = (uint)(guid & 0xFFFFFFFF);
            uint guidHigh = (uint)(guid >> 32);
    
            var asm = new[]
            {
                "push 0",
                "push " + guidHigh,
                "push " + guidLow,
                "push 0",
                "push " + spellid,
                "call " + CastSpell,
                "add esp, 0x14",
                "retn"
            };
    
            wManager.Wow.Memory.WowMemory.InjectAndExecute(asm);
        }
        catch (System.Exception ex)
        {
            Logging.WriteError("Error casting spell: " + ex.Message);
        }
    }

    Performance Optimization

    While repeated calls to GetSpellIdByName could introduce minor latency, this can be mitigated through result caching with cache invalidation upon skill acquisition or update events.

    Benefits

    • Eliminates dependency on the Lua CastSpellByName function, significantly reducing detection risk on Lua-hook protected servers
    • Supports targeted spell casting without requiring target changes


    User Feedback

    Recommended Comments

    nax

    Posted

    its not letting me update the main post, here is the code in you're syntext

    GetSpellIdByName :

        public static int GetSpellIdByName(string spellName, out int rank)
        {
    
            rank = 0;
            try
            {
                uint GetSpellIdByNameAddr = 0x00540200;
    
                // Allocate space for spell name string
                var nameBytes = System.Text.Encoding.UTF8.GetBytes(spellName + "\0");
                uint nameSpace = wManager.Wow.Memory.WowMemory.AllocData.Get(nameBytes.Length);
    
                if (nameSpace <= 0)
                    return 0;
    
                // Allocate space for rank (int = 4 bytes)
                uint rankSpace = wManager.Wow.Memory.WowMemory.AllocData.Get(4);
    
                // Allocate space to store the return value (spell ID from eax)
                uint spellIdSpace = wManager.Wow.Memory.WowMemory.AllocData.Get(4);
    
                if (nameSpace <= 0 || rankSpace <= 0 || spellIdSpace <= 0)
                    return 0;
    
                // Write spell name to memory
                wManager.Wow.Memory.WowMemory.Memory.WriteBytes(nameSpace, nameBytes);
                wManager.Wow.Memory.WowMemory.Memory.WriteInt32(rankSpace, 0);
                wManager.Wow.Memory.WowMemory.Memory.WriteInt32(spellIdSpace, 0);
    
                var asm = new[]
                {
                    wManager.Wow.Memory.WowMemory.CallWrapperCodeRebaseEsp(GetSpellIdByNameAddr, 8, nameSpace, rankSpace), 
                
                    // Store the spell ID (eax) to spellIdSpace
                    "mov ecx, " + spellIdSpace,
                    "mov [ecx], eax",
    
                   wManager.Wow.Memory.WowMemory.RetnToHookCode
                };
    
                wManager.Wow.Memory.WowMemory.InjectAndExecute(asm);
    
                // Read back the spell ID and rank
                int spellId = wManager.Wow.Memory.WowMemory.Memory.ReadInt32(spellIdSpace);
                rank = wManager.Wow.Memory.WowMemory.Memory.ReadInt32(rankSpace);
    
                // Free allocated memory
                wManager.Wow.Memory.WowMemory.AllocData.Free(nameSpace);
                wManager.Wow.Memory.WowMemory.AllocData.Free(rankSpace);
                wManager.Wow.Memory.WowMemory.AllocData.Free(spellIdSpace);
    
    
                return spellId;
            }
            catch (System.Exception ex)
            {
                Logging.WriteError("Error getting spell ID by name: " + ex.Message);
                return 0;
            }
        }

     

    CastSpell

     public static void CastSpell(int spellid, ulong guid = 0)
     {
         try
         {
             uint CastSpell = 0x080DA40;
             uint guidLow = (uint)(guid & 0xFFFFFFFF);
             uint guidHigh = (uint)(guid >> 32);
    
             var asm = new[]
             {
                 wManager.Wow.Memory.WowMemory.CallWrapperCodeRebaseEsp(CastSpell, 0x14, spellid , 0, guidLow, guidHigh, 0),
                 wManager.Wow.Memory.WowMemory.RetnToHookCode
             };
    
             wManager.Wow.Memory.WowMemory.InjectAndExecute(asm);
         }
         catch (System.Exception ex)
         {
             Logging.WriteError("Error casting spell: " + ex.Message);
         }
     }

     



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

×
×
  • Create New...