[Internal] Executing from the main thread without detours menu

User Tag List

Results 1 to 6 of 6
  1. #1
    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)

    [Internal] Executing from the main thread without detours

    Greetings. As we're all beginning work on a new game I really would like to get something clear from the beginning: There should be no need to detour functions JUST to execute code from the main thread. It's much nicer to gain access by executing WildStar's function "CEventQueue::Register". You provide it with a function and it will call it from the main thread after a given time interval. It is the perfect function for entering, and remaining in the main thread.

    Their polling function is called more often than (for example) DX9's EndScene or DX11's Present, so in most cases you will find yourself entering the main thread faster than using any kind of detour. And personally, I hate seeing DirectX hooks when they aren't needed.

    The function:

    CEventQueue::Register takes 4 parameters, including the instance of CEventQueue at ecx.

    1. this (ecx): Our best option is to create our own instance because of how easily it can be constructed. See below.
    2. interval: How long until the callback is called, in milliseconds. 0 is also acceptable if you want to enter instantly - it will be called on the next queue polling.
    3. event: A pointer to the CTimedEvent struct. See below.
    4. priority: Simply put, it does exactly what you'd expect. It's the priority of the event. See below.


    Code:
    typedef void(__thiscall *CEventQueue__Register_t)(CEventQueue *_this, uint32 interval, CTimedEvent *callback, EventPriority priority);
    CEventQueue__Register_t CEventQueue__Register;


    CEventQueue construction:

    The construction of a CEventQueue class is very simple (at least, for our needs!) Only the first field must match.

    Code:
    class CEventQueue {
    public:
        CEventQueue() {
            m_magic = (uint32) 'TNVE';
            ZeroMemory(&m_unk[0], sizeof(m_unk));
        }
    
    private:
        uint32 m_magic;
        uint8 m_unk[60]; // Total class size must be 64 bytes.
    };


    CTimedEvent construction:

    The construction of an event is also very easy. Thankfully, we don't need to go any deeper for these fields. The definition of the callback function should change, depending on whether the parameter value will be passed. See example below.

    Code:
    class CTimedEvent {
    public:
        BOOL UseParameter;
        void *Instance;
        void *Callback;
        void *Parameter;
    };


    EventPriority:

    This is the call order priority for events. Critical events are fired first, and lowest priority events are fired last.

    Code:
    enum class EventPriority {
        Lowest,
        BelowNormal,
        Normal,
        Highest,
        Critical
    };


    Unregistering events:

    We can quite easily remove events using CEventQueue::Remove. It takes no arguments, excluding the instance parameter in ecx which is present as with all __thiscall functions. This function is also thread-safe, so it does not necessarily need to be called from the main thread. If you support unloading of your injected library than it is absolutely necessary to remove events so there are no active events that will call back to invalid memory.

    Code:
    typedef void(__thiscall *CEventQueue__Remove_t)(CEventQueue *_this);


    Example:

    Code:
    typedef void(__thiscall *CEventQueue__Register_t)(CEventQueue *_this, uint32 interval, CTimedEvent *callback, EventPriority priority);
    CEventQueue__Register_t CEventQueue__Register;
    
    typedef void(__thiscall *CEventQueue__Remove_t)(CEventQueue *_this);
    CEventQueue__Remove_t CEventQueue__Remove;
    
    // Repeats forever @ 7500ms repeating intervals.
    CEventQueue *s_eventQueue1;
    CTimedEvent *s_event1;
    
    // Doesn't use parameter @ 2500ms.
    CEventQueue *s_eventQueue2;
    CTimedEvent *s_event2;
    
    // Uses parameter @ 5000ms.
    CEventQueue *s_eventQueue3;
    CTimedEvent *s_event3;
    
    // (Hopefully) never gets called.
    CEventQueue *s_eventQueue4;
    CTimedEvent *s_event4;
    
    void __fastcall RepeatingCallback(void *instance, void *_edx) {
        MessageBoxA(nullptr, "RepeatingCallback was called!", "Example callbacks", MB_OK | MB_ICONINFORMATION);
    
        // Requeue the event. Yay for main thread loops!
        CEventQueue__Register(s_eventQueue1, 7500, s_event1, EventPriority::Normal);
    }
    
    void __fastcall CallbackWithoutParameter(void *instance, void *_edx) {
        MessageBoxA(nullptr, "CallbackWithoutParameter was called!", "Example callbacks", MB_OK | MB_ICONINFORMATION);
    }
    
    void __fastcall CallbackWithParameter(void *instance, void *_edx, uint32 parameter) {
        std::stringstream message;
        message << "CallbackWithParameter was called!" << '\n';
        message << "Parameter: " << parameter;
        MessageBoxA(nullptr, message.str().c_str(), "Example callbacks", MB_OK | MB_ICONINFORMATION);
    }
    
    void Initialize() {
        CEventQueue__Register = reinterpret_cast<CEventQueue__Register_t>(Offsets::CEventQueue::Register);
        CEventQueue__Remove = reinterpret_cast<CEventQueue__Remove_t>(Offsets::CEventQueue::Remove);
    
        s_eventQueue1 = new CEventQueue();
        s_event1 = new CTimedEvent();
        s_event1->Instance = nullptr;
        s_event1->Callback = RepeatingCallback;
        s_event1->UseParameter = FALSE;
    
        s_eventQueue2 = new CEventQueue();
        s_event2 = new CTimedEvent();
        s_event2->Instance = nullptr;
        s_event2->Callback = CallbackWithoutParameter;
        s_event2->UseParameter = FALSE;
    
        s_eventQueue3 = new CEventQueue();
        s_event3 = new CTimedEvent();
        s_event3->Instance = nullptr;
        s_event3->Callback = CallbackWithParameter;
        s_event3->UseParameter = TRUE;
        s_event3->Parameter = (void*) 1234;
    
        s_eventQueue4 = new CEventQueue();
        s_event4 = new CTimedEvent();
        s_event4->Instance = nullptr;
        s_event4->Callback = nullptr; // Crashes our game.
        s_event4->UseParameter = FALSE;
    
        CEventQueue__Register(s_eventQueue1, 7500, s_event1, EventPriority::Normal);
        CEventQueue__Register(s_eventQueue2, 2500, s_event2, EventPriority::Normal);
        CEventQueue__Register(s_eventQueue3, 5000, s_event3, EventPriority::Normal);
        CEventQueue__Register(s_eventQueue4, 1000, s_event4, EventPriority::Normal);
    
        // The fourth event points to the callback function at 0x00000000, which doesn't exist.
        // This will crash our game, so we want to unregister it!
        CEventQueue__Remove(s_eventQueue4);
    }

    Congrats! You're in the main thread.
    Last edited by Jadd; 07-09-2014 at 10:10 PM.

    [Internal] Executing from the main thread without detours
  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)
    In case you haven't figured it out already, you can probably find the offsets in the latest info dump thread. I decided not to post them here in case people are still referring to this post in a hundred years. We wouldn't want to upset any copy-pasters .

    Edit: I guess I'll add some notes to this post, too.

    The first thing I can think of: if you are using a parameter in the callback, then the first two arguments are required. __fastcall passes the first two arguments via. ecx and edx registers, and the third and following parameters is where the arguments actually start: at ebp+8. If this is wrong, your function will probably mess up, and even worse it will pop the incorrect number of variables off the stack. This is exactly how it needs to be.

    On the other hand: if you aren't passing the parameter to the callback, it's totally valid to ignore definitions of ANY arguments in your call - even ecx and edx. Having a function defined as void __fastcall CallbackFunc() will work just fine. You could even use __cdecl if you aren't passing any arguments, but I wouldn't recommend it (there's really no need.)
    Last edited by Jadd; 07-09-2014 at 10:12 PM.

  3. #3
    JuceMMOCrawler's Avatar Sergeant
    Reputation
    45
    Join Date
    Mar 2014
    Posts
    45
    Thanks G/R
    0/1
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This is nice. :-*

  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)
    As pointed out by Apoc: the name for the event class is probably CTimedEvent, and also that the last parameter used by the Register function is event priority. As such, I've changed the names and added the EventPriority enum.

  5. #5
    Narache's Avatar Member
    Reputation
    13
    Join Date
    Dec 2007
    Posts
    36
    Thanks G/R
    6/7
    Trade Feedback
    1 (100%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yea !
    Thanks you very much Jadd, it works like a charm

    You must spread some Reputation around before giving it to Jadd again.

  6. #6
    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)
    Added CEventQueue::Remove info.

Similar Threads

  1. [Bot] Injection code into wow. Do you have to call functions from the main thread?
    By Miivers in forum World of Warcraft Bots and Programs
    Replies: 2
    Last Post: 01-13-2014, 02:56 PM
  2. Dumping the function names from the WoW executable?
    By Tanaris4 in forum WoW Memory Editing
    Replies: 3
    Last Post: 08-08-2009, 10:51 AM
  3. Out of the main thread
    By Shamun in forum WoW Memory Editing
    Replies: 11
    Last Post: 12-20-2008, 06:36 AM
  4. Replies: 2
    Last Post: 09-12-2008, 08:51 PM
  5. How to get the assistance you want from the thread you make
    By degoscar in forum WoW EMU Guides & Tutorials
    Replies: 0
    Last Post: 05-26-2008, 03:18 PM
All times are GMT -5. The time now is 08:50 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