Tutorial Madden NFL 20 Hack - Hooking Input - Frostbite Engine

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

timb3r

Semi-Retired
Dank Tier VIP
Jul 15, 2018
768
22,668
47


Game: Madden NFL 20
Engine: Frostbite
Studio: EA
Buy: Origin

Introduction:

EA: where do I even start? EA has a reputation as one of the worst game companies around and for good reason. If someone told me EA was using child labor to run their acid mines I'd probably believe you. In case you need a refresher on their evil deeds over the years checkout the video below:

"They also killed Westwood: unforgivable".

Today we're continuing on with hooking game input. If you didn't see the previous tutorial on hooking Direct Input 8 you can read that here. So while I was messing around with Tomb Raider @pcG jumped into the chat and asked me what the best way would be to emulate input (since I was trash talking SendInput and the like). I said that hooking Direct Input or the Window Procedure was probably the most efficient way if your code is running inside the process.

We then proceeded to attempt to hook the input of his game of choice: Madden NFL 20. What followed was a long drawn out process whereby we tried to nail-down exactly how the game was processing input. Madden loads dinput8.dll so the assumption was it processes input that way: nope. I then hooked the window procedure and blocked the input messages the game still received input.

Solving the conundrum:

So after all the obvious (read: ways Microsoft recommends you acquire input) I tried to get in the mindset of an EA employee. I work 13 hour days, my wife left me because I never take time off work because EA runs like a prison. This project is 3 months behind schedule and it needs to be out in time for summer. My butthole hurts because I've been thoroughly reamed by the company bull. There's a bug in our input processing code: FUCK IT: USE GetAsyncKeyState.

The most ghetto solution is the correct one. I'm unsure if this applies to all Frostbite Engine games but Madden NFL 20 actually contains a bunch of strings from other games. It also has a bunch of random code and other garbage in there that it never touches. Frostbite is confirmed shitcode.


This is what Frostbite looks like in x64dbg or IDA.

I didn't actually bother reversing the game too much but it looks as though Frostbite loops through an array forever checking the state of various keys to see if they've been pressed then fires the game logic for that key-press. The good news is this is a really unsophisticated way of gathering input and trivial to hook.

Injecting code:

For whatever reason the hardest part of this entire thing was the issues I had actually getting my code injected. My injection tool failed, the GuidedHacking Injector failed, x64dbg couldn't even load the damn library. I even attempted to modify the IAT but the game only ran once and then never ran again after that. Usually I'd look into exactly why this stuff was happening but I wasn't interested I just wanted my damn code in there so I could get the input.

The solution I ended up using was a ProxyDLL which I consider to be the best way to inject code into a process. In my case I noticed that Madden's IAT had an entry for version.dll which is super easy to proxy because most processes only use three functions:
  • GetFileVersionInfoSizeA
  • VerQueryValueA
  • GetFileVersionInfoA
We can easily forward or proxy those functions it only takes about 15-20 minutes to get a skeleton up and running. If you're interested in how Proxying a DLL works go checkout my Vulkan tutorial which covers it in more detail.

GetAsyncKeyState:

The function prototype for GetAsyncKeyState is:

C++:
SHORT GetAsyncKeyState(
  int vKey
);
It's pretty simple. Like most usermode functions it's a wrapper around NtUserGetAsyncKeyState which lives in win32u.dll. There's plenty of options for hooking this call such as using detours, or minihook or some other library. But as I was too lazy to build my project with detours and since my ProxyDLL loads early in the execution chain we patch this function with MOV RAX, JMP RAX hook:

C++:
/*
    48 B8 - MOV RAX, 0ffffffffffffffffh ; This is our placeholder value
    FF E0 - JMP RAX
*/

// Typedef for GetAsyncKeyState
typedef SHORT(WINAPI* GetAsyncKeyState_t)(int);
GetAsyncKeyState_t fnGetAsyncKeyState = NULL, , fnNtGetAsyncKeyState = NULL;

// The address to detour
fnGetAsyncKeyState = (GetAsyncKeyState_t)GetProcAddress(LoadLibrary(L"user32.dll"), "GetAsyncKeyState");
// The address we're going to call when we've finished
fnNtGetAsyncKeyState = (GetAsyncKeyState_t)GetProcAddress(LoadLibrary(L"win32u.dll"), "NtUserGetAsyncKeyState");

uint8_t patch[] = { 0x48, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0 };

// Move the pointer to start of the jmp address
uintptr_t* hookAddr = (uintptr_t*)(patch + 2);
// Overwrite the placeholder value with our hook address
*hookAddr = (uintptr_t)GetAsyncKeyState_Hook;

// Change memory prots
if (!VirtualProtect(fnGetAsyncKeyState, 12, PAGE_EXECUTE_READWRITE, &oldProt)) {
    printf("[-] VP Failed: %d\n", GetLastError());
    return -1;
}

// Copy bytes
memcpy(fnGetAsyncKeyState, patch, 12);

// Change it back to the original values
if (!VirtualProtect(fnGetAsyncKeyState, 12, oldProt, &oldProt)) {
    printf("[-] VP Failed: %d\n", GetLastError());
    return -1;
}
Now that we're hooked we can modify the passed values and the return codes. Our hook look like this:

Madden20.exe -> GetAsyncKeyState -> JMP GetAsyncKeyState_Hook -> CALL NtUserGetAsyncKeyState.

The original function is overwritten so we must call NtUserGetAsyncKeyState from our hook.

Here comes the interesting part.

Emulating key presses:

Blocking input is simple enough:

C++:
SHORT GetAsyncKeyState_Hook(int vKey)
{
    if (vKey == VK_SPACE){
        printf("[X] VK_SPACE\n");
        return 0;
    }
    return fnNtGetAsyncKeyState(vKey);
}
vKey is the Virtual Key Code the host process is checking. Full list here.

However because they're calling this function in a loop we need a way to time our key-presses so they seem natural. In the DInput tutorial I used GetTickCount64 to time my button presses and it will probably work here too.

So if we follow the same rough format we can implement something like so:

C++:
SHORT GetAsyncKeyState_Hook(int vKey)
{
    // Our data structures for key injection
    static bool bShouldInject = false;
    static ULONGLONG startTime = 0, endTime = 0;
    struct KEYDATA
    {
        int vKey;
        DWORD duration;
    };
    static KEYDATA keys[256];
    static int currKey = 0;
    static int totalKeys = 0;

    // Timing check
    endTime = GetTickCount64();

    // Delete will be our trigger key
    short keyPress = fnNtGetAsyncKeyState(VK_DELETE);

    if (bShouldInject && vKey == keys[currKey].vKey &&
        (endTime - startTime) > keys[currKey].duration)
    {
        printf("[+] Sending key: %d\n", keys[currKey].vKey);

        startTime = GetTickCount64();

        if (currKey++ >= totalKeys) {
            printf("Injection finished.\n");
            bShouldInject = false;
        }

        return -32767; // The 'magic' return value
    }
    else if (keyPress && !bShouldInject)
    {
        printf("[+] Injecting keys..\n");
        // Delete was pressed
        bShouldInject = true;

        // Send the right arrow key three times
        // Pausing for a 1s in between
        keys[0].vKey = VK_RIGHT;
        keys[0].duration = 0;

        keys[1].vKey = VK_RIGHT;
        keys[1].duration = 1000;

        keys[2].vKey = VK_RIGHT;
        keys[2].duration = 1000;

        currKey = 0;
        totalKeys = 2;

        // Reset the clock
        startTime = GetTickCount64();
    }

    return fnNtGetAsyncKeyState(vKey);
}
Wait for the game to launch and reach the main menu: now tap Delete you should have the right arrow key tapped twice with a 1s interval in between. Here's an earlier prototype that switched back and forth between VK_LEFT and VK_RIGHT:


And that's the "correct" way to simulate key presses in Frostbite.

Final result:
Capture.PNG
 
Last edited:

kino0924

Full Member
Dec 31, 2019
11
114
0
Is there specific reason to detoure to NtUserGetAsyncKeyState but not original GetAsyncKeyState?
 

mambda

headass
Escobar Tier VIP
Trump Tier Donator
Jun 25, 2014
2,275
37,938
268
Is there specific reason to detoure to NtUserGetAsyncKeyState but not original GetAsyncKeyState?
i just skimmed the thread but it shows that hes hooked GetAsyncKeyState, what are you talking about?
 

timb3r

Semi-Retired
Dank Tier VIP
Jul 15, 2018
768
22,668
47
Is there specific reason to detoure to NtUserGetAsyncKeyState but not original GetAsyncKeyState?
Because my jump hook destroys the original function I directly call the nt big boy api because we're essentially patching out that function. Most usermode api call a corresponding nt api after they've done some sanity checking.

If you used a hooking library they'd fix up the code for you but I was too lazy to impliment that when a couple of bytes did the job.
 

kino0924

Full Member
Dec 31, 2019
11
114
0
i just skimmed the thread but it shows that hes hooked GetAsyncKeyState, what are you talking about?
When I use detour library, usually i call back original function.
Madden20.exe -> GetAsyncKeyState_Hook -> GetAsyncKeyState.

But this example clearly calls NtUserGetAsyncKeyState in GetAsyncKeyState_Hook and he also mentioned in the post
Madden20.exe -> GetAsyncKeyState -> JMP GetAsyncKeyState_Hook -> CALL NtUserGetAsyncKeyState.

Anyway timb3r answered why he did it. I was just curious if there was any other logical reason to call NtUserGetAsyncKeyState
But simply it was due to GetAsyncKeyState was broken because timb3r didnt restore original code of GetAsyncKeyState lol
 
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods League of Legends Accounts