Skip to content
View in the app

A better way to browse. Learn more.

WRobot

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

[Security] CastSpellByName

  • 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

WRobot user

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);
     }
 }

 

Hello,

Thanks for the suggestion.

There's already a protection in place that handles this case, and so far it's been holding up well against Lua-side detection.

If you need this on a specific server, a plugin or a fight class override is the cleaner route.

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.