Reversing CastSpell menu

User Tag List

Results 1 to 12 of 12
  1. #1
    Reghero's Avatar Member
    Reputation
    11
    Join Date
    Jun 2017
    Posts
    35
    Thanks G/R
    29/7
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Reversing CastSpell

    I'm having problems calling this function:

    Code:
    __int64 __fastcall Spell_C_CastSpell_0(unsigned int a1, __int64 a2, __int64 a3, __int64 a4)
    {
      __int64 v8; // rax
    
      v8 = ClntObjMgrGetActivePlayer();
      return Spell_C_CastSpell(v8, a1, a3, a4, a2, 0i64);
    }
    Located at 00F78AF0 in 39475.

    My assumption is that the arguments are (guessing from a previous dump as I can't analyse Spell_C_CastSpell:

    castSpell(spellId, ?, ?, targetGuid)

    The targetGuid is purely a guess based on previous clients, I think Hex Rays has failed to get a4 correct because of the analysis failure for Spell_C_CastSpell.

    Calling it with the above parameters crashes the game.

    Reversing CastSpell
  2. #2
    Jadd's Avatar 🐸 Premium Seller
    Reputation
    1511
    Join Date
    May 2008
    Posts
    2,432
    Thanks G/R
    81/333
    Trade Feedback
    1 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    aaaaaaaaaaaaaaaaaa

    Code:
    [return: MarshalAs(UnmanagedType.I1)]
    public delegate bool Spell_C_CastSpell(int spellId, ref CSpellCastParameters parameters, IntPtr item, ObjectGuid guid);
    
    ...
    
    [StructLayout(LayoutKind.Sequential)]
    public struct CSpellCastParameters {
        /* 0x00 */ public long Unk_0x00;
        /* 0x08 */ public int Difficulty;
        /* 0x0C */ public long Unk_0x0C;
        /* 0x14 */ public int Unk_0x14;
        /* 0x18 */ public int Unk_0x18;
        /* 0x1C */ public int Unk_0x1C;
    }
    Not sure what the struct is, never really bothered to reverse it. I just use an empty one (zeroed)

  3. Thanks Reghero (1 members gave Thanks to Jadd for this useful post)
  4. #3
    Reghero's Avatar Member
    Reputation
    11
    Join Date
    Jun 2017
    Posts
    35
    Thanks G/R
    29/7
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Jadd View Post
    aaaaaaaaaaaaaaaaaa

    Code:
    [return: MarshalAs(UnmanagedType.I1)]
    public delegate bool Spell_C_CastSpell(int spellId, ref CSpellCastParameters parameters, IntPtr item, ObjectGuid guid);
    
    ...
    
    [StructLayout(LayoutKind.Sequential)]
    public struct CSpellCastParameters {
        /* 0x00 */ public long Unk_0x00;
        /* 0x08 */ public int Difficulty;
        /* 0x0C */ public long Unk_0x0C;
        /* 0x14 */ public int Unk_0x14;
        /* 0x18 */ public int Unk_0x18;
        /* 0x1C */ public int Unk_0x1C;
    }
    Not sure what the struct is, never really bothered to reverse it. I just use an empty one (zeroed)
    Cheers.

    Is there some thread safety that needs to be done around this? I can run it perfectly as long as I don't click on the window, as soon as I click on the WoW window, the game crashes.

    Code:
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
            public delegate bool CastSpell(int spellId, ref CastSpellParameters parameters, IntPtr item, UInt128 guid);
    
            public static readonly CastSpell CastSpellFunction = (CastSpell)Marshal.GetDelegateForFunctionPointer(System.Diagnostics.Process.GetCurrentProcess().MainModule.BaseAddress + (int)Offsets.Functions.CastSpell, typeof(CastSpell));
            
    
    
    var spell = new CastSpellParameters();
                    ThreadSynchronizer.RunOnMainThread(
                        () => Functions.CastSpellFunction(
                            castSpellRequest.SpellId,
                            ref spell,
                            IntPtr.Zero,
                            castSpellRequest.Target));
    I've tried with and without the RunOnMainThread method (shamelessly ripped from https://drewkestell.us/).
    Last edited by Reghero; 07-27-2021 at 09:08 AM.

  5. #4
    _chase's Avatar Established Member
    Reputation
    95
    Join Date
    Dec 2019
    Posts
    57
    Thanks G/R
    16/49
    Trade Feedback
    0 (0%)
    Mentioned
    5 Post(s)
    Tagged
    0 Thread(s)
    Think it would be a good idea to first remove any possible synchronization issues by either using directx11 legacy mode in the client and executing in directx11's present to be on the main thread, or hooking WndProc which you can find an article/tutorial about here: ntoskrnl | Hooking Threads Without Detours or Patches (article courtesy of @Jadd)

    And then I'm not sure about the cast spell you are looking at, but I cast spells like the below. But, this won't work for item spells so it's solely for actual abilities.
    Haven't tested this for a little, because was working on other things in my bot but did a quick search around and for the below FindSlotBySpellId looks to be at 0x157CB30 and CastSpell at 0x157A7B0 for tbc 39475

    Code:
    	int32_t GetSpellSlotFromId(int32_t spellId)
    	{
    		return reinterpret_cast<int32_t(__fastcall*)(int32_t, int32_t)>(Module::BaseAddress() + Offsets::FindSlotBySpellId)(spellId, 0);
    	}
    
    	void CastSpellBySlot(int32_t spellSlot, WowGuid* targetGuid)
    	{
    		reinterpret_cast<int64_t(__fastcall*)(int32_t, int32_t, WowGuid*, uint8_t, uint8_t)>(Module::BaseAddress() + Offsets::CastSpell)(spellSlot, 0, targetGuid, 0, 0);
    	}
    
    	bool CastSpellById(int32_t spellId, WowGuid* targetGuid)
    	{
    		if (!IsSpellKnown(spellId)) {
    			Logger::Log(("Attempted to cast spell id: " + std::to_string(spellId) + ", but is not known").c_str());
    			return false;
    		}
    
    		int32_t spellSlot = GetSpellSlotFromId(spellId);
    
    		if (spellSlot < 0)
    			return false;
    
    		CastSpellBySlot(spellSlot, targetGuid);
    		return true;
    	}
    Last edited by _chase; 07-28-2021 at 08:25 AM.

  6. #5
    Reghero's Avatar Member
    Reputation
    11
    Join Date
    Jun 2017
    Posts
    35
    Thanks G/R
    29/7
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by _chase View Post
    Think it would be a good idea to first remove any possible synchronization issues by either using directx11 legacy mode in the client and executing in directx11's present to be on the main thread, or hooking WndProc which you can find an article/tutorial about here: ntoskrnl | Hooking Threads Without Detours or Patches (article courtesy of @Jadd)

    And then I'm not sure about the cast spell you are looking at, but I cast spells like the below. But, this won't work for item spells so it's solely for actual abilities.
    Haven't tested this for a little, because was working on other things in my bot but did a quick search around and for the below FindSlotBySpellId looks to be at 0x157CB30 and CastSpell at 0x157A7B0 for tbc 39475

    Code:
    	int32_t GetSpellSlotFromId(int32_t spellId)
    	{
    		return reinterpret_cast<int32_t(__fastcall*)(int32_t, int32_t)>(Module::BaseAddress() + Offsets::FindSlotBySpellId)(spellId, 0);
    	}
    
    	void CastSpellBySlot(int32_t spellSlot, WowGuid* targetGuid)
    	{
    		reinterpret_cast<int64_t(__fastcall*)(int32_t, int32_t, WowGuid*, uint8_t, uint8_t)>(Module::BaseAddress() + Offsets::CastSpell)(spellSlot, 0, targetGuid, 0, 0);
    	}
    
    	bool CastSpellById(int32_t spellId, WowGuid* targetGuid)
    	{
    		if (!IsSpellKnown(spellId)) {
    			Logger::Log(("Attempted to cast spell id: " + std::to_string(spellId) + ", but is not known").c_str());
    			return false;
    		}
    
    		int32_t spellSlot = GetSpellSlotFromId(spellId);
    
    		if (spellSlot < 0)
    			return false;
    
    		CastSpellBySlot(spellSlot, targetGuid);
    		return true;
    	}
    Cheers for the article, I mentioned before I'm using the ThreadSynchronizer which under the hood uses the same concept:

    Thread sync . GitHub

    I was actually previously using the methods that you've shared above but had the same result. They work fine until I interact with the Wow window again (click it for example), then the game will crash.

    I should mention this is classic.
    Last edited by Reghero; 07-28-2021 at 04:28 PM.

  7. #6
    oiramario's Avatar Established Member
    Reputation
    85
    Join Date
    Mar 2021
    Posts
    133
    Thanks G/R
    36/51
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think CastSpellBySlot(spellSlot, targetGuid) should be CastSpellBySlot(spellSlot, targetAddr).
    It's object addr not guid pointer.

  8. #7
    Reghero's Avatar Member
    Reputation
    11
    Join Date
    Jun 2017
    Posts
    35
    Thanks G/R
    29/7
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've narrowed it down to the threading.

    Using both:

    ntoskrnl | Hooking Threads Without Detours or Patches

    Or the previous gist I mentioned. If I hook the Window it will crash wow if I interact with it in any way (clicking, resizing etc) regardless of trying to call any funcs.

  9. #8
    Jadd's Avatar 🐸 Premium Seller
    Reputation
    1511
    Join Date
    May 2008
    Posts
    2,432
    Thanks G/R
    81/333
    Trade Feedback
    1 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    This was written back when everyone was still using x86. I've been meaning to make some amendments for the last... well, a long time.

    Try using the x64 definitions (also changed to unicode variant for some other issues down the line):
    Code:
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);
    
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern IntPtr SendMessage(IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);
    You can also consider System.Windows.Threading.Dispatcher for a cleaner approach. You simply need to access the main thread context via. WndProc, then you can store the dispatcher from Dispatcher.CurrentDispatcher.

    There's probably a way to do it without hooking WndProc at all, but I haven't really cared to look - I use that hook for hotkeys anyway.

    Code:
    public static class Window {
        #region Fields
        private static IntPtr _originalWindowProc;
        private static WindowProc _customWindowProc;
        #endregion
    
        #region Properties
        public static IntPtr Handle { get; private set; }
        #endregion
        
        internal static void Initialize() {
            AttachLocalWndProc(Engine.Process.MainWindowHandle);
            while (!Context.Initialized) { }
        }
    
        internal static void Shutdown() {
            SetWindowLongPtr(Handle, -4 /* GWL_WNDPROC */, _originalWindowProc);
        }
    
        internal static void AttachLocalWndProc(IntPtr windowHandle) {
            Handle = windowHandle;
    
            _customWindowProc = OnWndProc;
            _originalWindowProc = SetWindowLongPtr(Handle, -4 /* GWL_WNDPROC */, Marshal.GetFunctionPointerForDelegate(_customWindowProc));
    
            SendMessage(Handle, WindowMessage.Paint, IntPtr.Zero, IntPtr.Zero);
        }
    
        private static int OnWndProc(IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam) {
            if (!Context.Initialized)
                Context.Initialize();
            
            // (stripped) hotkey handling also done here.
    
            return CallWindowProc(_originalWindowProc, hWnd, Msg, wParam, lParam);
        }
    }
    Code:
    public static class Context {
        #region Properties
        public static bool Initialized { get; private set; }
        #endregion
    
        #region Fields
        private static Dispatcher _dispatcher;
        private static int _threadId;
        #endregion
    
        internal static void Initialize() {
            _dispatcher = Dispatcher.CurrentDispatcher;
            _threadId = GetCurrentThreadId();
            Initialized = true;
        }
    
        public static void Dispatch(Action callback) {
            if (GetCurrentThreadId() != _threadId)
                _dispatcher.Invoke(callback);
            else
                callback();
        }
    
        public static TResult Dispatch<TResult>(Func<TResult> callback) {
            if (GetCurrentThreadId() != _threadId)
                return _dispatcher.Invoke(callback);
            else
                return callback();
        }
    Then you can synchronously execute on the main thread, for example:
    Code:
    var spellId = ...;
    var spellName = Context.Dispatch(() => CGSpellBook__GetSpellName(spellId));

  10. Thanks Reghero (1 members gave Thanks to Jadd for this useful post)
  11. #9
    Jadd's Avatar 🐸 Premium Seller
    Reputation
    1511
    Join Date
    May 2008
    Posts
    2,432
    Thanks G/R
    81/333
    Trade Feedback
    1 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Also FYI (though I don't think they still do this) they had some detection in the past by checking the DirectX device's refcount from within common functions that would be used by a bot - Spell_C_CastSpell, click to move functions, etc.

    They could simply see that the device is mid-render, which would normally never happen. This would set a flag in .data which would later be subtly included in frequently sent packets. Best to avoid it, though the same case could probably be made for a WndProc hook.

  12. Thanks _chase (1 members gave Thanks to Jadd for this useful post)
  13. #10
    zys924's Avatar Active Member
    Reputation
    20
    Join Date
    Nov 2009
    Posts
    113
    Thanks G/R
    0/7
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Jadd View Post
    Also FYI (though I don't think they still do this) they had some detection in the past by checking the DirectX device's refcount from within common functions that would be used by a bot - Spell_C_CastSpell, click to move functions, etc.

    They could simply see that the device is mid-render, which would normally never happen. This would set a flag in .data which would later be subtly included in frequently sent packets. Best to avoid it, though the same case could probably be made for a WndProc hook.
    Curious, just ask have you managed to locate the actual opcode of your mentioned "sent packets"?

  14. #11
    Reghero's Avatar Member
    Reputation
    11
    Join Date
    Jun 2017
    Posts
    35
    Thanks G/R
    29/7
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ah, this was it! I really need to keep in my mind the x64 nature when reading historical samples

    Here's the completed class, I updated my existing one only to find the memory lib I'm using (GitHub - lolp1/Process.NET: A C# class library for interacting with processes.) already had a base class that could be inherited from - nice and clean! I quite like the dispatcher snippet you've posted so I'll probably incorporate that in too.

    gist:8b167c981eedf89f9c4a5c6e1e3eb479 . GitHub

    Thanks for the heads up also.
    Last edited by Reghero; 07-29-2021 at 03:09 AM.

  15. #12
    Jadd's Avatar 🐸 Premium Seller
    Reputation
    1511
    Join Date
    May 2008
    Posts
    2,432
    Thanks G/R
    81/333
    Trade Feedback
    1 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by zys924 View Post
    curious, just ask have you managed to locate the actual opcode of your mentioned "sent packets"?
    It was in CMSG_AUTH_CONTINUED_SESSION some time during 7.2

  16. Thanks Reghero (1 members gave Thanks to Jadd for this useful post)

Similar Threads

  1. Account sold, money reversed. Need help
    By odoacer in forum World of Warcraft General
    Replies: 14
    Last Post: 11-29-2007, 02:06 PM
  2. Reverse Gamecard scam.
    By rooco in forum WoW Scam Prevention
    Replies: 4
    Last Post: 11-19-2007, 02:00 PM
  3. {guide} guide on not being scammed via reverse payment
    By *TraPStaR* in forum WoW Scam Prevention
    Replies: 0
    Last Post: 11-12-2007, 07:53 AM
  4. 'Funneh Troll Edit' reversed
    By eddy9994 in forum World of Warcraft Model Editing
    Replies: 4
    Last Post: 05-18-2007, 02:17 PM
  5. Reverse Birthday Scam
    By Solemio in forum WoW Scam Prevention
    Replies: 8
    Last Post: 08-02-2006, 04:09 PM
All times are GMT -5. The time now is 05:45 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search