Tutorial Stack Flow Hijacking - Seizing control and hiding code

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

timb3r

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


Game: Shadow Warrior
Engine: Road Hog Engine
Studio: Flying Wild Hog
Version: Steam
Buy: Steam

Introduction:

Stack Flow Hijacking is a technique I developed to both hijack and redirect execution flow of a remote process and execute custom code within the context of the stack. Why would you need to do this? Because it's cool.


Yep.

Is this a new or novel technique? Unknown I have seen similar techniques being utilized by loaders / packers but I've never seen anyone do it in this particular way.

Will this help you bypass certain types of protections? Maybe 🤷 I've not actually tested it on anything serious I just wanted to demonstrate how it works and how you can do it yourself. Then who knows maybe someone can develop the technique further and do cool stuff with it.

So lets talk about the stack:

Simply put the stack is a a chunk of memory that gets allocated for each thread in a process. Values can pushed or popped off the stack as the program sees fit however the stack has some interesting attributes you may not be aware of. By default a stack is usually allocated with the following protections: -RW-G- (Full Access). That's nothing earth shattering you'd expect a stack to be both readable and writable however when I was reversing Steam's DRM I noticed something interesting:

main-thread-stack.PNG

Yes, you see correct that's an executable page of memory inside the stack.

Part of steam's loader allocates / re-purposes this page in order to execute the following code:

steam.PNG


The cool thing is this page is 0x1000 bytes in size meaning we can write quite a sizable chunk of code here. I've written a small program that will quickly scan a game's memory searching for this executable page in memory.

C++:
uintptr_t GetExploitablePage(HANDLE process, bool ignoreSig)
{
    for (uintptr_t i = 0; i < 0x00007fffffffffff; i++)
    {
        MEMORY_BASIC_INFORMATION mbi = { 0 };

        if (!VirtualQueryEx(process, (LPCVOID)i, &mbi, sizeof(MEMORY_BASIC_INFORMATION)))
            continue;

        if (mbi.Protect & PAGE_EXECUTE_READ)
        {
            if (ignoreSig)
                return (uintptr_t)mbi.BaseAddress;

            const uint8_t pattern[] =
            {
                0x9C, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41,
                0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57
            };

            uint8_t buffer[25] = { 0 };

            ReadProcessMemory(process, (LPCVOID)mbi.BaseAddress, buffer, 25, NULL);

            bool match = true;
            for (int i = 0; i < 25; i++)
            {
                if (buffer[i] != pattern[i]) {
                    match = false; break;
                }
            }

            if (match) {
                return (uintptr_t)mbi.BaseAddress;
            }
        }

        i += mbi.RegionSize;
    }

    return 0;
}
I'll also do a pattern check on the code above to make certain it has the right location in memory.

So we can easily find this page on run-time in case the game doesn't have a fixed base address (luckily for us Shadow Warrior does have a fixed base address). So you may be wondering why this is such a big deal? Most protection systems will search for regions of memory with the executable flag set and flag them as suspicious however they typically leave the stack alone as it's difficult to checksum it effectively and can be quite a performance nightmare for real time programs like games (That's not to say they NEVER do it, it's just not that common afaik). Also since this was set by Valve it's probably white-listed or ignored (I have no idea if this is the case be careful).

So Valve have left us this nice chunk of executable memory that we can write code into however if we're going to remain stealthy we need to remain hidden. Creating a thread or installing a hook into the .text section isn't an option we need to get sneaky.

Note:
  1. This appears to only be common within x64 steam games.
  2. I only tested it on two games so your mileage may vary.
Getting Sneaky:

So I had the first component in place a chunk of executable memory I could write into now I needed a way to execute it. Since all the regular methods were out I started thinking stack based shenanigans. We can write to the stack meaning we can overwrite any values that are stored there, in order for this to be useful we need to find some exploitable code within the game to pull this off.

Doing a command search (CTRL+F) shows that CALL RAX is very common within this game engine.

whoa.PNG


Essentially I just ran the game and started setting breakpoints until we tripped a function that was called over and over again.

Once the breakpoint had tripped it revealed this function:
pivot.png


Stack:
regs.PNG


Which was de-referencing this pointer here:
stack.PNG


The cool thing is for this particular game anyway this location never changes in memory so we can be lazy and read the memory directly at 0x14F738. So 0x14F738 points at 0x9152578 which in turn points to 0x140448A40 (target game function). Since the function is always at a fixed point in memory we can use an absolute address which makes our shellcode very small and efficient:

Code:
    ; Module base address
    mov rax, 0000000140000000h

    ; This uses the longest and worst pointer chain ever......but it works
    mov rax, [rax + 00D25F38h]
    mov rax, [rax + 78h]
    mov rax, [rax + 18h]
    mov rax, [rax + 8h]
    mov rax, [rax + 1A8h]
    mov rax, [rax + 48h]
    mov rax, [rax + 28h]
    add rax, 1340h

    ; Compare our money to 100k if its not equal set it
    cmp qword ptr [rax], 47C35000h
    je done
    mov qword ptr [rax], 47C35000h
done:  
    mov rax, 0000000140000000h
    add rax, 448A40h
    ; Jump back to the original game function
    jmp rax
Now we can write this code to the exploitable page we found, pause the game's threads and overwrite the function pointer with one to our code.

Here's what it looks like in action:

finished.png

"You can see the money value has been updated and note: the application isn't crashing xD".

Outtro:

And that's pretty much all there is to it! To find if a game is vulnerable check for the executable page within the stack and then locate some exploitable code inside the game which executes a pointer off the stack and you're in business. I tried to make this example as simple as possible to make explaining it easier obviously things like ASLR and what not will complicate things but they can easily be worked around. Also in the code provided below I overwrote Valve's code with my own you'd probably avoid doing that in the real world.

Code:
#include <stdio.h>
#include <windows.h>
#include <inttypes.h>
#include <tchar.h>

int main(void)
{
    const TCHAR processName[] = TEXT("sw.x64.exe");

    HANDLE gameProcess = OpenProcessByName(processName);
    if (!gameProcess) {
        wprintf(L"[-] Unable to open process: %s\n", processName);
        return -1;
    }
    else {
        wprintf(L"[+] Opened: %s (%x)\n", processName, GetProcessId(gameProcess));
    }

    DWORD threadArray[255] = { 0 };
    int threadCount = GetThreadList(gameProcess, threadArray);
    if (threadCount <= 0)
    {
        wprintf(L"[-] Failed to get threads for %s\n", processName);
    }
    else {
        wprintf(L"[+] %s has %d threads.\n", processName, threadCount);
    }

    uintptr_t pageAddress = GetExploitablePage(gameProcess, true);
    if (!pageAddress) {
        wprintf(L"[-] No exploitable pages found.\n");
        CloseHandle(gameProcess);
        return -1;
    }

    wprintf(L"[+] Found exploitable page at: %llx\n", pageAddress);

    const uint8_t unlimitedMoneyShellcode[]
    {
            0x48, 0xB8, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x80, 0x38, 0x5F, 0xD2,
            0x00, 0x48, 0x8B, 0x40, 0x78, 0x48, 0x8B, 0x40, 0x18, 0x48, 0x8B, 0x40, 0x08, 0x48, 0x8B, 0x80,
            0xA8, 0x01, 0x00, 0x00, 0x48, 0x8B, 0x40, 0x48, 0x48, 0x8B, 0x40, 0x28, 0x48, 0x05, 0x40, 0x13,
            0x00, 0x00, 0x48, 0x81, 0x38, 0x00, 0x50, 0xC3, 0x47, 0x74, 0x07, 0x48, 0xC7, 0x00, 0x00, 0x50,
            0xC3, 0x47, 0x48, 0xB8, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x48, 0x05, 0x40, 0x8A,
            0x44, 0x00, 0xFF, 0xE0
    };

    if (!WriteProcessMemory(gameProcess, (LPVOID)pageAddress, (LPCVOID)&unlimitedMoneyShellcode, sizeof(unlimitedMoneyShellcode), NULL))
    {
        wprintf(L"[-] Unable to write shellcode aborting.\n");
        CloseHandle(gameProcess);
        return -1;
    }

    wprintf(L"[+] Writing shellcode to: %llx\n", pageAddress);

    const uintptr_t stackAddr = 0x000000000014F738;
    uintptr_t baseAddr   = GetRemoteBaseAddress(gameProcess, const_cast<TCHAR*>(processName));
    uintptr_t remoteFunc = baseAddr + 0x448A40;
    uintptr_t funcVal = 0, funcPtr = 0;

    while (funcVal != remoteFunc)
    {
        ReadProcessMemory(gameProcess, (LPCVOID)stackAddr, &funcPtr, sizeof(uintptr_t), NULL);
        ReadProcessMemory(gameProcess, (LPCVOID)funcPtr, &funcVal, sizeof(uintptr_t), NULL);
    }

    wprintf(L"[+] Found function pointer at: %llx\n", funcPtr);

    wprintf(L"[?] Suspending target threads...\n");
    for (int i = 0; i < threadCount; i++)
    {
        HANDLE thread = OpenThread(THREAD_ALL_ACCESS | THREAD_QUERY_INFORMATION, false, threadArray[i]);
        SuspendThread(thread);
        CloseHandle(thread);
    }

    if (!WriteProcessMemory(gameProcess, (LPVOID)funcPtr, (LPCVOID)&pageAddress, sizeof(uintptr_t), NULL))
        wprintf(L"[-] Unable to hijack execution.\n");
    else
        wprintf(L"[+++] Bad Ptr set now we wait.\n");

    wprintf(L"[?] Resuming target threads...\n");
    for (int i = 0; i < threadCount; i++)
    {
        HANDLE thread = OpenThread(THREAD_ALL_ACCESS | THREAD_QUERY_INFORMATION, false, threadArray[i]);
        ResumeThread(thread);
        CloseHandle(thread);
    }

    CloseHandle(gameProcess);
    return 0;
}
I've left out some code to prevent pasting you smart cookies should be able to substitute the relevant code quite easily.
 
Last edited:

Sigmaa

Meme Tier VIP
Full Member
Top Poster Of Month
Dec 14, 2018
266
3,228
10
you're really just injecting code into the stack aren't you, you piece of shit. fuck you. jk, this is actually really cool. why the fuck would you even put an executable page into the stack? loll
 
  • Like
Reactions: Samb0

Samb0

.?AVtype_info@@
Dank Tier Donator
Sep 30, 2019
25
403
1
you're really just injecting code into the stack aren't you, you piece of shit. fuck you. jk, this is actually really cool. why the fuck would you even put an executable page into the stack? loll
And why wouldnt you clean up after finished using it 😮 I thought steams loader cleaned up after itself, apparently not.
 
  • Haha
Reactions: metrix

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,573
78,998
2,316
This tutorial was just released from the premium forum, donate to get early access to our tutorials
 
  • Like
Reactions: Samb0
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