C# FrameScript__RegisterFunction research and issues menu

User Tag List

Results 1 to 13 of 13
  1. #1
    opulent's Avatar Member
    Reputation
    5
    Join Date
    Apr 2009
    Posts
    29
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    C# FrameScript__RegisterFunction research and issues

    This method of registering a lua callback has been covered on a few occasions and although I've spent the last few days (more like months on and off) searching for the solution to this bug, it continues to painfully allude me. The function appears to register but when I call it using something like

    Code:
    FrameScript_Execute("FooInput("CastSpellByName(\"Shadow Bolt\"));

    My toon casts the spell (verified by dead mob on relog) but the application crashes with reference to "FooInput" as the addon in the crash log. I'm guessing I'm doing something retarded but I can't seem to see what it is.

    How it's done:

    Basically I'm trying to write a jmp operand E9 MY AD DR ES somewhere in the .text section of the executable which jumps to the delegate of my callback function. The memory address I choose for the patch is definately within the bounds of the .text segment of the executable's memory. My method of finding these 5 bytes of 0x0 is via a sort of randomized sigscan (Not starting the scan from 0x1000 either because I feel that it's safer further from that boundary with limited randomization). I've also used many hardcoded offsets but the same crash occurs.

    Here's some of the code:
    Credit goes to Apoc for his .NET implementation and everyone else that contributed
    to understanding this portion of Wow's internals.

    Code:
        public class LuaManager
        {
            private static FScriptExecuteDelegate _execute;
            private static FScriptGetTopDelegate _getTop;
            private static FScriptRegisterDelegate _register;
            private static CallbackDelegate _callback;
            private static FScriptToLStringDelegate _toLString;
    
            /// <summary>
            /// Initializes a new instance of the <see cref="LuaManager"/> class.
            /// </summary>
            public LuaManager()
            {
                _luaValues = new List<string>();
                
                // Function Pointer Initialization Stuff. 
                _execute = Memory.RegisterDelegate<FScriptExecuteDelegate>
                    (Memory.Patterns["FrameScript_Execute"]);
                _getTop = Memory.RegisterDelegate<FScriptGetTopDelegate>
                    (Memory.Patterns["FrameScript_GetTop"]);
                _register = Memory.RegisterDelegate<FScriptRegisterDelegate>
                    (Memory.Patterns["FrameScript_RegisterFunction"]);
                _toLString = Memory.RegisterDelegate<FScriptToLStringDelegate>
                    (Memory.Patterns["FrameScript_ToLString"]);
    
                // This is required to get a pointer to the
                // callback (used in _register)
                _callback = CallbackHandler;
                RegisterCallback();
            }
    
            private List<string> _luaValues;
            /// <summary>
            /// Injects a string of lua into the games framescript engine
            /// </summary>
            /// <param name="strCommand">The lua command.</param>
            public void DoString(string strCommand)
            {
                _execute(strCommand, "IPittyTheFoo.lua", 0);
            }
    
            /// <summary>
            /// Registers a lua function called "FooInput"
            /// </summary>
            public void RegisterCallback()
            {         
                // This installs a patch (a jump) to my CallbackHandler
                // in the .text section of the client.
                IntPtr patchAddress = Memory.Patterns["FrameScript_PatchAddress"];
                IntPtr callbackAddress = Marshal.GetFunctionPointerForDelegate(_callback);
    
                FooModule.Logs["Debug"].AddMessage(string.Format("Patch address : {0}", patchAddress));
                FooModule.Logs["Debug"].AddMessage(string.Format("Callback address: {0}", callbackAddress));
    
                List<byte> bytes = new List<byte>(); 
    
                // Generating the Byte array...
                bytes.Add(0xE9); // jmp statement
                byte[] temp = BitConverter.GetBytes((uint) callbackAddress);
    
                // Adding the address bytes in reverse order
                for (int i = temp.Length - 1; i >= 0; i--)
                {
                    bytes.Add(temp[i]);
                }
    
                Memory.PatchManager.CreateAndApply(patchAddress, bytes.ToArray(), "RegisterFunction");
    
                _register("FooInput", patchAddress);
            }
    
            private int CallbackHandler(IntPtr pLuaState)
            {
                FooModule.Logs["Debug"].AddMessage("Lua callback called");
                _luaValues.Clear();
                int num = _getTop(pLuaState);
                for (int i = 0; i < num; i++)
                {
                    string tmp = ToLString(pLuaState, i);
                    _luaValues.Add(tmp);
                }
                return 0;
            }
    
            private string ToLString(IntPtr pLuaState, int index)
            {
                return _toLString(pLuaState, index + 1, 0);
            }
    
            /// <summary>
            /// Executes a string of lua and returns the 
            /// values of the function as a string array.
            /// </summary>
            /// <param name="lua">The lua string.</param>
            /// <returns></returns>
            public string[] GetReturnVal(string lua)
            {
                DoString(String.Format("MaiaInput({0})", lua));
                return _luaValues.ToArray();
            }
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate int FScriptExecuteDelegate(string lua, string fileName, uint pState);
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate uint FScriptRegisterDelegate(string szName, IntPtr pFunc);
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate string FScriptToLStringDelegate(IntPtr pLuaState, int idx, int length);
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate int CallbackDelegate(IntPtr pLuaState);
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            private delegate int FScriptGetTopDelegate(IntPtr pLuaState);
    
    Well.. that's it, I'm not sure if it's something totally retarded that I'm missing and I'd sure appreciate a fresh set of eyes if you can spot anything that might yield a clue pertaining to the cause.

    Also, just for the sake of documenting something so that this thread might be useful for something I think the pointer check looks like this...

    Code:
    unsigned int FrameScript__InvalidPtrCheck(unsigned int * myFunctionPointer)
    {
        int * lowBoundary; 
        int * highBoundary;
      
        lowBoundary = (int*)(D872E8);
        highBoundary = (int*)(D872EC);
    
        if ( !lowBoundary || !highBoundary )
        {
            sub_7D6300(); // Press CTRL + R and try again
            lowBoundary = (int*)(D872E8);
            highBoundary = (int*)(D872EC);
        }
    
        if ( myFunctionPointer < lowBoundary || myFunctionPointer >= highBoundary )
        {
            throw("Invalid function pointer... rawr!");
        }
      
        return lowBoundary;
    }
    
    Everytime I try to chart the XREFS to this function my IDA crashes, I'm assuming that It gets called somewhere in the Execute chain because I can't seem to find it referenced in FrameScript::RegisterFunction.

    I'm also assuming that these two (D872E8) and (D872EC) hold pointers to the start and end of the .text segment but I'm not 100% sure.

    Any help of advice would be highly appreciated.



    Related Threads :

    Highly relevant
    http://www.mmowned.com/forums/world-...ml#post1567311

    http://www.mmowned.com/forums/world-...callbacks.html

    http://www.mmowned.com/forums/world-...ml#post1436155

    http://www.mmowned.com/forums/world-...ml#post1707663

    Moderately relevant
    http://www.mmowned.com/forums/world-...ml#post1879902

    Not so relevant really
    http://www.mmowned.com/forums/world-...-commands.html

    http://www.mmowned.com/forums/world-...functions.html

    C# FrameScript__RegisterFunction research and issues
  2. #2
    _Mike's Avatar Contributor
    Reputation
    310
    Join Date
    Apr 2008
    Posts
    531
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Code:
    bytes.Add(0xE9); // jmp statement
    byte[] temp = BitConverter.GetBytes((uint) callbackAddress);
    // Adding the address bytes in reverse order
    for (int i = temp.Length - 1; i >= 0; i--)
    {
      bytes.Add(temp[i]);
    }
    E9 is a relative jump but you're giving it an absolute address.

  3. #3
    Syltex's Avatar Sergeant Major
    Reputation
    23
    Join Date
    Jul 2010
    Posts
    174
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The syntax on the Lua code looks wierd/unfininshed.
    Code:
    FrameScript_Execute("FooInput("CastSpellByName(\"Shadow Bolt\"));
    Why not use?:
    FrameScript_Execute("CastSpellByName(\"Shadow Bolt\")");

  4. #4
    _Mike's Avatar Contributor
    Reputation
    310
    Join Date
    Apr 2008
    Posts
    531
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Syltex View Post
    The syntax on the Lua code looks wierd/unfininshed.
    Code:
    FrameScript_Execute("FooInput("CastSpellByName(\"Shadow Bolt\"));
    Why not use?:
    FrameScript_Execute("CastSpellByName(\"Shadow Bolt\")");
    Yeah it's non-functional. From briefly reading the OP I believe he calls it like
    FrameScript_Execute("FooInput(CastSpellByName(\"Shadow Bolt\"))");
    which would explain why the cast is successful before the crash.

  5. #5
    Syltex's Avatar Sergeant Major
    Reputation
    23
    Join Date
    Jul 2010
    Posts
    174
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by _Mike View Post
    Yeah it's non-functional. From briefly reading the OP I believe he calls it like
    FrameScript_Execute("FooInput(CastSpellByName(\"Shadow Bolt\"))");
    which would explain why the cast is successful before the crash.
    Yeah, knew that but i wanned him to think of his own

  6. #6
    opulent's Avatar Member
    Reputation
    5
    Join Date
    Apr 2009
    Posts
    29
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by _Mike View Post
    E9 is a relative jump but you're giving it an absolute address.
    Thankyou so much..

    This works like a charm
    Code:
    
        List<byte> bytes = new List<byte> { 0x68 };
        byte[] tmp = BitConverter.GetBytes(callbackAddress.ToInt32());
        bytes.AddRange(tmp);
        bytes.Add(0xC3);

    The moral of the story -> Relative jump is relative.

    This issue is resolved.

  7. #7
    noctural's Avatar Active Member Captain Copypasta CoreCoins Purchaser
    Reputation
    26
    Join Date
    Apr 2009
    Posts
    76
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This post was extremely helpful for me to understand full circle how a lua callback works, thanks. I think I was in the same boat as you, thinking about registering my own callback for a few months now.

    Anyhow, I have two questions that I hope are straight forward enough, but I wasn't able to find a clear answer searching the forums.

    1.) can patchAddress be any 5 consecutive bytes in the .text region that are either 0x90 or 0x0?
    2.) when injecting a .net dll, is it possible to load it without a bootstrapper dll?

    Thanks again for the post!

  8. #8
    PyGuy's Avatar Corporal
    Reputation
    14
    Join Date
    Jan 2011
    Posts
    20
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by noctural View Post
    1.) can patchAddress be any 5 consecutive bytes in the .text region that are either 0x90 or 0x0?
    2.) when injecting a .net dll, is it possible to load it without a bootstrapper dll?
    1) It needs to be 5 bytes with the JMP command or 6 bytes using the PUSH [address] RET commands. You can also scan for consecutive 0xCC (INT3) bytes which are typically placed between functions in the .text region by the compiler to align the function to a boundary.
    2) I believe you need the bootstrapper dll. My understanding is that a C# dll by itself does not contain any code to start the CLR. Since Wow doesn't start the CLR for you, you need a way to do that.

  9. #9
    noctural's Avatar Active Member Captain Copypasta CoreCoins Purchaser
    Reputation
    26
    Join Date
    Apr 2009
    Posts
    76
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So, from his example, I don't understand the 0xC3 return.

    List<byte> bytes = new List<byte> { 0x68 }; <-- jmp 1st byte
    byte[] tmp = BitConverter.GetBytes(callbackAddress.ToInt32()); <-- address 2nd,3rd,4th,5th bytes
    bytes.AddRange(tmp);
    bytes.Add(0xC3); <-- A 6th byte to retn? Is this necessary?

  10. #10
    MaiN's Avatar Elite User
    Reputation
    335
    Join Date
    Sep 2006
    Posts
    1,047
    Thanks G/R
    0/10
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by noctural View Post
    So, from his example, I don't understand the 0xC3 return.

    List<byte> bytes = new List<byte> { 0x68 }; <-- jmp 1st byte
    byte[] tmp = BitConverter.GetBytes(callbackAddress.ToInt32()); <-- address 2nd,3rd,4th,5th bytes
    bytes.AddRange(tmp);
    bytes.Add(0xC3); <-- A 6th byte to retn? Is this necessary?
    The first byte is 'push'. The retn instructions pops a value from the stack into EIP (call pushes the EIP before changing IP).
    [16:15:41] Cypher: caus the CPU is a dick
    [16:16:07] kynox: CPU is mad
    [16:16:15] Cypher: CPU is all like
    [16:16:16] Cypher: whatever, i do what i want

  11. #11
    noctural's Avatar Active Member Captain Copypasta CoreCoins Purchaser
    Reputation
    26
    Join Date
    Apr 2009
    Posts
    76
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks MaiN. So when a call is made to callbackAddress by the LUA engine, it will push the EIP. I've learned more from this thread in the past day than I have in the past few months. Mainly due to the OP "How it's done:" explanation.. it just made it all click.

    Previously, I had a native dll that upon injection/dll_attach , would suspend wow's thread, set a hardware breakpoint, then register a vectored exception handler for the hwbp address. The exception handler would then change values of registers and commit those changes. The result is that the 'protected lua function' check effectively returns 'not protected'.

    This had a few problems..sometimes after the dll was injected.. the vectored exception handler did nothing.. so it wouldn't work. Ejecting the dll and re-injecting still wouldn't work. I imagine there's some race condition by doing all this logic in the dll_attach..and sometimes it worked.. sometimes it didn't. That was very annoying, so I was looking for a more fool proof way to do this.

    In comes registered callbacks! As the OP suggested, I used WhiteMagic to do all the low level win32/patching manipulation, which is a godsend for someone with a lot of c# experience but 0 unmanaged/ptr manipulation/low level experience. I got a chance to see how it all works in a language I know..and it helped my understanding of what's going on under the hood.

    Today, I got a callback registered, so that when called in game, the parameter gets popped off the stack and then executed with framescript__execute.

    I'm very excited.

    Some things for me to learn/figure out this weekend:

    1.) How to make a byte pattern to use with WhiteMagic, so that I can get offsets in a more generic way for frame_execute, et al. I'm not sure which bytes are "meaningful" and which should be ?? , etc.
    2.) I saw a post by cypher that suggested to not even patch the game client to jmp, but rather register an address that when called will throw an exception. Then register a VEH, so when the exeption occurs, the VEH will call the actual callback. This seems very cool, but I'd have to figure out how to create a VEH from .NET.

  12. #12
    robertf's Avatar Private
    Reputation
    1
    Join Date
    Feb 2011
    Posts
    2
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by opulent View Post
    I'm also assuming that these two (D872E and (D872EC) hold pointers to the start and end of the .text segment but I'm not 100% sure.
    Yes. These two globals are generated dynamically if either of them is NULL; a quick hack is setting them to 0x1 and (unsigned int)-1 respectively.

    btw, I suggest you go in process if you don't mind. I am currently using injected dll and hooking EndScene to get "pulses", then I can call FrameScript::RegisterFunction without the concern of race condition. I don't think warden is able to detect your dll if your project is private (anti-virus application injects dlls, fraps hooks EndScene).
    Last edited by robertf; 02-11-2011 at 04:20 PM.

  13. #13
    MaiN's Avatar Elite User
    Reputation
    335
    Join Date
    Sep 2006
    Posts
    1,047
    Thanks G/R
    0/10
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by noctural View Post
    Thanks MaiN. So when a call is made to callbackAddress by the LUA engine, it will push the EIP. I've learned more from this thread in the past day than I have in the past few months. Mainly due to the OP "How it's done:" explanation.. it just made it all click.
    It has nothing to do with LUA. The x86 'call' instruction works by pushing EIP to the stack then changing EIP. The x86 'ret' instruction works by popping the stack and then setting EIP.
    [16:15:41] Cypher: caus the CPU is a dick
    [16:16:07] kynox: CPU is mad
    [16:16:15] Cypher: CPU is all like
    [16:16:16] Cypher: whatever, i do what i want

Similar Threads

  1. [Development Research and debate] Battlefield: Wow Server
    By crewd in forum World of Warcraft Emulator Servers
    Replies: 9
    Last Post: 04-05-2009, 11:09 PM
  2. Connection issue, server is running and realm list is correct.
    By Ghosthopper in forum World of Warcraft Emulator Servers
    Replies: 20
    Last Post: 01-31-2008, 12:40 PM
  3. Legal Issues and Bannable Offenses.
    By Hallowsend in forum World of Warcraft Guides
    Replies: 10
    Last Post: 12-28-2007, 11:29 AM
  4. [Question] Upper And Lower Torso Issue
    By Xcynic in forum WoW ME Questions and Requests
    Replies: 1
    Last Post: 10-14-2007, 08:35 AM
  5. Gnome to Undead - Helm and Mouth gap issue
    By michael1991 in forum WoW ME Questions and Requests
    Replies: 2
    Last Post: 05-30-2007, 07:16 AM
All times are GMT -5. The time now is 09:09 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search