__fastcall, EasyHook and C#/.NET menu

User Tag List

Results 1 to 10 of 10
  1. #1
    Neverhaven's Avatar Member
    Reputation
    12
    Join Date
    Sep 2009
    Posts
    25
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    __fastcall, EasyHook and C#/.NET

    I know this isn't strictly World of Warcraft, but it seems like I'll get the best answers here, and the questions isn't exactly Warcraft III exclusive.

    I'm working on a modding API for Warcraft III, and for that I need to hook some __fastcall functions. So far I've used EasyHook and Marshalling with great success. However, hooking a __fastcall function is proving a bit troublesome. Largely because I'm very new to assembler.

    The idea is I inject a __fastcall function that calls my managed method. The managed method then calls the original __fastcall function and returns the result. I have some code which almost works. Usually two calls succeed before it crashes, but sometimes only one, and I'm hoping someone might be willing to take a look at the code, and tell me if you see any glaring errors?

    The event viewer gives me the exception code: 0xc0000005, which is an Access Violation. The fault offset is always 0x0082f618, even if the base pointer for game.dll and codecave pointers changes.
    Code:
        // ObjectIdL is a wrapper for a char[4] which is the type and parent ids, which are always ascii characters.
    
        // int __fastcall RegisterType(ObjectIdL type, ObjectIdL parent, ClassPrototype *prototype)
        private const Int32 REGISTER_TYPE_OFFSET = 0x4716D0;
        private IntPtr RegisterTypePtr;
    
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate IntPtr RegisterTypeWrapperHookPrototype(ObjectIdL a1, ObjectIdL a2, ClassPrototypePtr a3);
        private IntPtr RegisterTypeWrapperHookPtr;
    
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate IntPtr RegisterTypeWrapperPrototype(ObjectIdL a1, ObjectIdL a2, ClassPrototypePtr a3);
        private RegisterTypeWrapperPrototype RegisterTypeWrapper;
        private IntPtr RegisterTypeWrapperPtr;
    
        private IntPtr RegisterTypeHookWrapperPtr;
    
        private LocalHook hook;
    
        public override void OnGameReady()
        {
            this.RegisterTypePtr = Kernel32.GetModuleHandle("game.dll") + REGISTER_TYPE_OFFSET;
    
            this.RegisterTypeWrapperHookPtr = Utility.FunctionAsPtr(new RegisterTypeWrapperHookPrototype(this.RegisterTypeWrapperHook));
    
            this.AssembleHookWrapper(this.RegisterTypeWrapperHookPtr);
            this.AssembleWrapper(this.RegisterTypePtr);
    
    
            this.hook = LocalHook.CreateUnmanaged(this.RegisterTypePtr, this.RegisterTypeHookWrapperPtr, IntPtr.Zero);
            this.hook.ThreadACL.SetInclusiveACL(new[] { 0 });
    
            MessageBox.Show("RegisterTypeHookWrapperPtr: 0x" + this.RegisterTypeHookWrapperPtr.ToString("X8"));
            MessageBox.Show("RegisterTypeWrapperPtr: 0x" + this.RegisterTypeWrapperPtr.ToString("X8"));
    
            base.OnGameReady();
        }
    
        // This is a __fastcall body calling a __cdecl function.
        public void AssembleHookWrapper(IntPtr hook)
        {
            this.RegisterTypeHookWrapperPtr = Kernel32.VirtualAlloc(IntPtr.Zero, 1024, AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
    
            var fasm = new FasmNet();
    
            fasm.AddLine("use32");
            fasm.AddLine("pop ebx");    // store the return address in ebx
    
            fasm.AddLine("push ecx");
            fasm.AddLine("push edx");
            fasm.AddLine("call {0}", hook);
            fasm.AddLine("add esp, 8");
    
            fasm.AddLine("push ebx");   // restore the return address from ebx
            fasm.AddLine("retn");
    
            var code = fasm.Assemble(this.RegisterTypeHookWrapperPtr);
            Marshal.Copy(code, 0, this.RegisterTypeHookWrapperPtr, code.Length);
        }
    
        // This is a __cdecl body calling a __fastcall function.
        public void AssembleWrapper(IntPtr original)
        {
            this.RegisterTypeWrapperPtr = Kernel32.VirtualAlloc(IntPtr.Zero, 1024, AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
    
            var fasm = new FasmNet();
    
            fasm.AddLine("use32");
            fasm.AddLine("pop ebx");    // store the return address in ebx
    
            fasm.AddLine("pop ecx");
            fasm.AddLine("pop edx");
            fasm.AddLine("call {0}", original);
            fasm.AddLine("sub esp, 12");
    
            fasm.AddLine("push ebx");   // restore the return address from ebx
            fasm.AddLine("retn");
    
            var code = fasm.Assemble(this.RegisterTypeWrapperPtr);
            Marshal.Copy(code, 0, this.RegisterTypeWrapperPtr, code.Length);
            this.RegisterTypeWrapper = Utility.PtrAsFunction<RegisterTypeWrapperPrototype>(this.RegisterTypeWrapperPtr);
        }
    
        private IntPtr RegisterTypeWrapperHook(ObjectIdL type, ObjectIdL parent, ClassPrototypePtr prototype)
        {
            var classPrototype = prototype.AsUnsafe();
            Trace.WriteLine("RegisterTypeWrapperHook(" + type + ", " + parent + ", " + prototype.AsIntPtr().ToString("X8") + ")");
            Trace.WriteLine(" ClassSize -> " + classPrototype->BatchSize);
            Trace.WriteLine(" BatchSize -> " + classPrototype->ElementsCreatedCount);
    
            var result = this.RegisterTypeWrapper(type, parent, prototype);
    
            Trace.WriteLine(" result -> " + result.ToString("X8"));
    
            // This textbox shows up twice, both times the Trace show the expected data.
            MessageBox.Show("sub_6F4716D0WrapperHook");
            return result;
        }

    __fastcall, EasyHook and C#/.NET
  2. #2
    namreeb's Avatar Legendary

    Reputation
    658
    Join Date
    Sep 2008
    Posts
    1,023
    Thanks G/R
    7/215
    Trade Feedback
    0 (0%)
    Mentioned
    8 Post(s)
    Tagged
    0 Thread(s)
    I have posted some code for how I do this in this thread: http://www.ownedcore.com/forums/worl...mp-thread.html ([WoW] 1.12.1.5875 Info Dump Thread)

  3. #3
    Neverhaven's Avatar Member
    Reputation
    12
    Join Date
    Sep 2009
    Posts
    25
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It looks very promising. Do you have anything like a CreateFromFastcall?

    I notice that you enter a stack-frame always, but only leave a stack-frame when there's more than two arguments. Why is that?
    Last edited by Neverhaven; 02-16-2014 at 05:32 AM. Reason: Stupidity?

  4. #4
    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 Neverhaven View Post
    It looks very promising. Do you have anything like a CreateFromFastcall?

    I notice that you enter a stack-frame always, but only leave a stack-frame when there's more than two arguments. Why is that?
    The first two arguments in (GCC) fastcall functions are passed via. ecx and edx.

  5. #5
    Neverhaven's Avatar Member
    Reputation
    12
    Join Date
    Sep 2009
    Posts
    25
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Jadd View Post
    The first two arguments in (GCC) fastcall functions are passed via. ecx and edx.
    Yeah, but I was referring to the
    Code:
    push ebp
    mov ebp, esp
    and
    Code:
    mov esp, ebp
    pop ebp
    The first enters the stack-frame, and the other leaves it. But due to conditions, the leave only happens when there are more than two arguments, that is, arguments on the stack. I would assume if you enter the stack-frame, you should always leave it, but maybe that isn't the case.

  6. #6
    Valediction's Avatar Active Member
    Reputation
    37
    Join Date
    Jul 2012
    Posts
    48
    Thanks G/R
    8/4
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I posted a modification of Grey Magic to support hooking of Fastcall functions, you can look it up in this same forum. Grey Magic already supports detouring of all the other calling conventions (which in turn are supported natively in the .NET Framework) so you could give that a try.

    EDIT: here ([RELEASE] Modification of GreyMagic to support detouring of fastcalls)
    Last edited by Valediction; 02-16-2014 at 08:03 AM. Reason: Add link

  7. #7
    Neverhaven's Avatar Member
    Reputation
    12
    Join Date
    Sep 2009
    Posts
    25
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'll give it a look. Perhaps I could simply switch entirely to GreyMagic for my project.

  8. #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)
    Originally Posted by Neverhaven View Post
    Yeah, but I was referring to the
    Code:
    push ebp
    mov ebp, esp
    and
    Code:
    mov esp, ebp
    pop ebp
    The first enters the stack-frame, and the other leaves it. But due to conditions, the leave only happens when there are more than two arguments, that is, arguments on the stack. I would assume if you enter the stack-frame, you should always leave it, but maybe that isn't the case.
    Sorry, I read the question wrong. I presume it's a mistake? I see what you mean.

  9. #9
    Neverhaven's Avatar Member
    Reputation
    12
    Join Date
    Sep 2009
    Posts
    25
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Alright, with help and information gathered from your links, I've cooked it down to the following utility class.
    Code:
    public static class FastCall
    {
        public static IntPtr InvokePtr { get; private set; }
    
        private static Byte[] InvokeCode = new Byte[]
            {
                0x5A,                           // pop edx
                0x36, 0x87, 0x54, 0x24, 0x08,   // xchg ss:[esp+08],edx
                0x58,                           // pop eax
                0x59,                           // pop ecx
                0xFF, 0xE0                      // jmp eax
            };
    
        private static Byte[] WrapperCode = new Byte[]
            {
                0x58,                           // pop eax
                0x52,                           // push edx
                0x51,                           // push ecx
                0x50,                           // push eax
                0x68, 0x00, 0x00, 0x00, 0x00,   // push ...
                0xC3                            // retn
            };
    
        static FastCall()
        {
            FastCall.InvokePtr = Kernel32.VirtualAlloc(IntPtr.Zero, FastCall.InvokeCode.Length, AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
    
            Marshal.Copy(FastCall.InvokeCode, 0, FastCall.InvokePtr, FastCall.InvokeCode.Length);
        }
    
        public static IntPtr WrapStdCallInFastCall(IntPtr stdCallPtr)
        {
            var result = Kernel32.VirtualAlloc(IntPtr.Zero, FastCall.WrapperCode.Length, AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
    
            Marshal.Copy(FastCall.WrapperCode, 0, result, FastCall.WrapperCode.Length);
            Marshal.WriteIntPtr(result, 5, stdCallPtr);
    
            return result;
        }
    }
    Code:
        // int __fastcall RegisterType(ObjectIdL type, ObjectIdL base, ClassPrototype *prototype)
        private const Int32 REGISTER_TYPE_OFFSET = 0x4716D0;
        private IntPtr RegisterTypePtr;
    
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate IntPtr RegisterTypeWrapperHookPrototype(ObjectIdL a1, ObjectIdL a2, ClassPrototypePtr a3);
        private IntPtr RegisterTypeHookPtr;
    
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        private delegate IntPtr RegisterTypeWrapperPrototype(IntPtr functionPtr, ObjectIdL a1, ObjectIdL a2, ClassPrototypePtr a3);
        private RegisterTypeWrapperPrototype RegisterTypeWrapper;
    
        private LocalHook hook;
    
        public override void OnGameReady()
        {
            this.RegisterTypePtr = Kernel32.GetModuleHandle("game.dll") + REGISTER_TYPE_OFFSET;
    
            this.RegisterTypeWrapper = Utility.PtrAsFunction<RegisterTypeWrapperPrototype>(FastCall.InvokePtr);
            this.RegisterTypeHookPtr = FastCall.WrapStdCallInFastCall(Utility.FunctionAsPtr(new RegisterTypeWrapperHookPrototype(this.RegisterTypeHook)));
    
            this.hook = LocalHookEx.CreateUnmanaged(this.RegisterTypePtr, this.RegisterTypeHookPtr, IntPtr.Zero);
            this.hook.ThreadACL.SetInclusiveACL(new[] { 0 });
    
            base.OnGameReady();
        }
    
        private IntPtr RegisterTypeHook(ObjectIdL type, ObjectIdL parent, ClassPrototypePtr prototype)
        {
            try
            {
                var result = this.RegisterTypeWrapper(this.RegisterTypePtr, type, parent, prototype);
    
                //if (type.ToString() == "wscd")
                    //Trace.WriteLine("RegisterTypeWrapperHook(" + type + ", " + parent + ", " + prototype.AsIntPtr().ToString("X8") + ")");
    
                return result;
            }
            catch (Exception e)
            {
                Trace.WriteLine("RegisterTypeWrapperHook: " + e.ToString());
            }
            return IntPtr.Zero;
        }
    Now, the code is working perfectly when I comment out the two lines inside the hook. If I uncomment them, I'll get a crash at a random time. Sometimes It'll print two lines, sometimes it'll print thirty lines. I'm unsure how exactly to debug this or what could cause it. I did try and throw some Thread.Sleeps in there to see if there were any racing conditions, but that still worked fine, unless I added the Trace.

  10. #10
    namreeb's Avatar Legendary

    Reputation
    658
    Join Date
    Sep 2008
    Posts
    1,023
    Thanks G/R
    7/215
    Trade Feedback
    0 (0%)
    Mentioned
    8 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Jadd View Post
    Sorry, I read the question wrong. I presume it's a mistake? I see what you mean.
    Yeah that is just a mistake on my part.

Similar Threads

  1. Diablo 3 Login Error 3 and battle.net.ddl version different than expected
    By anticamper666 in forum Diablo 3 Emulator Servers
    Replies: 5
    Last Post: 06-14-2013, 06:29 AM
  2. [Revert] Patch 7 Diablo III.exe and battle.net.dll
    By madkilah28 in forum Diablo 3 General
    Replies: 0
    Last Post: 12-19-2011, 05:43 AM
  3. PayPal for Diablo III and Battle.net
    By HI5 in forum Diablo 3 General
    Replies: 2
    Last Post: 09-14-2011, 01:40 PM
  4. [Trading] Steam Account and Battle.Net account with SC2 Beta for WoW
    By fa1th09 in forum General Trading Buy Sell Trade
    Replies: 0
    Last Post: 06-21-2010, 06:27 PM
  5. Easyhook and whitemagic problems & FSM design question
    By !@^^@! in forum WoW Memory Editing
    Replies: 9
    Last Post: 02-16-2010, 06:51 AM
All times are GMT -5. The time now is 10:59 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