17

About the Program

I have a program which writes to the memory of the game I'm experimenting with. the code works for me just fine when I use a regular static address, but for some reason I can't seem to do it once I find a working pointer. For example, I find this in Cheat Engine after pointer scanning a few times:

enter image description here

This address works every time I load the game and edit the value. The problem is I don't understand how to use it in my program. Here's my declared variables which I attempted to plug in the values:

bool UnlimitedAmmo = false;
string AmmoPointer = "031B7324"; // <--- The address
int[] AmmoOffset = { 0x2c, 0x1e8, 0x3c8, 0x6d4, 0x508 }; // <--- It's pointers
int AmmoToFill = 1337; // <--- The Amount of ammo to give

I'm passing in these variables as follows:

MyMemory.ReadProcess = MyProcess[0];
MyMemory.Open();

int PointerAddress = HexToDec(AmmoPointer);
int[] PointerOffest = AmmoOffset;
int BytesWritten;
byte[] ValueToWrite = BitConverter.GetBytes(AmmoToFill);
string WrittenAddress = MyMemory.PointerWrite((IntPtr)PointerAddress, ValueToWrite, 
  PointerOffest, out BytesWritten);
MyMemory.CloseHandle();

I once used a static address (for a different game) and my code worked fine once I plugged the address and offset. I'm stumped this time. Any help and explanations would be deeply appreciated. Thanks in advance.

Community
  • 1
  • 1
Jake
  • 257
  • 1
  • 3
  • 11
  • I started a bounty on this question as I am currently trying to figure out the same thing. Namely, how to get the address of `THREADSTACK0` in C#. I have found some information on how it is created in cheatengine, and I can read the address in cheatengine, but none of the threadstartaddresses I am pulling in c# match what CE returns – user1274820 Oct 01 '17 at 20:23
  • See here: http://forum.cheatengine.org/viewtopic.php?p=5487976 – user1274820 Oct 01 '17 at 20:26

1 Answers1

15

I figured I would post a solution for this for people in the future.

One way you can handle this if you don't want to dive into the C++ code saved there and rewrite in C# is to simply use this program on github:

https://github.com/makemek/cheatengine-threadstack-finder

The direct download link is here:

https://github.com/makemek/cheatengine-threadstack-finder/files/685703/threadstack.zip

You can pass this executable a process ID and parse out the thread address you need.

Basically, what I did is my process runs the exe, redirects the output, and parses it.

Then the process closes and we do what we need - I sort of feel like I'm cheating, but it works.

The output for threadstack.exe typically looks like this:

PID 6540 (0x198c)
Grabbing handle
Success
PID: 6540 Thread ID: 0x1990
PID: 6540 Thread ID: 0x1b1c
PID: 6540 Thread ID: 0x1bbc
TID: 0x1990 = THREADSTACK 0 BASE ADDRESS: 0xbcff8c
TID: 0x1b1c = THREADSTACK 1 BASE ADDRESS: 0x4d8ff8c
TID: 0x1bbc = THREADSTACK 2 BASE ADDRESS: 0x518ff8c

Here is the code I ultimately used to get the address I need:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);

////////////////////////////////////////////////////////////////////
// These are used to find the StardewValley.Farmer structure     //
//////////////////////////////////////////////////////////////////
private IntPtr Thread0Address;
private IntPtr FarmerStartAddress;
private static int[] FARMER_OFFSETS = { 0x4, 0x478, 0x218, 0x24C };
private static int FARMER_FIRST = 0x264;
//////////////////////////////////////////////////////////////////

private async void hookAll()
{
    SVProcess = Process.GetProcessesByName("Stardew Valley")[0];
    SVHandle = OpenProcess(ProcessAccessFlags.All, true, SVProcess.Id);
    SVBaseAddress = SVProcess.MainModule.BaseAddress;
    Thread0Address = (IntPtr) await getThread0Address();
    getFarmerStartAddress();
}
private Task<int> getThread0Address()
{
    var proc = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = "threadstack.exe",
            Arguments = SVProcess.Id + "",
            UseShellExecute = false,
            RedirectStandardOutput = true,
            CreateNoWindow = true
        }
    };
    proc.Start();
    while (!proc.StandardOutput.EndOfStream)
    {
        string line = proc.StandardOutput.ReadLine();
        if (line.Contains("THREADSTACK 0 BASE ADDRESS: "))
        {
            line = line.Substring(line.LastIndexOf(":") + 2);
            return Task.FromResult(int.Parse(line.Substring(2), System.Globalization.NumberStyles.HexNumber));
        }
    }
    return Task.FromResult(0);
}
private void getFarmerStartAddress()
{
    IntPtr curAdd = (IntPtr) ReadInt32(Thread0Address - FARMER_FIRST);
    foreach (int x in FARMER_OFFSETS)
        curAdd = (IntPtr) ReadInt32(curAdd + x);
    FarmerStartAddress = (IntPtr) curAdd;
}
private int ReadInt32(IntPtr addr)
{
    byte[] results = new byte[4];
    int read = 0;
    ReadProcessMemory(SVHandle, addr, results, results.Length, out read);
    return BitConverter.ToInt32(results, 0);
}

Final

If you're interested in updating the C++ code, I believe the relevant portion is here.

It actually doesn't look too complicated - I think you're just grabbing the base address of the kernal32.dll and looking for that address in the thread stack by checking to see if it is >= to the base address or <= to the base address + size while reading each 4 bytes - I would have to play with it though.

DWORD GetThreadStartAddress(HANDLE processHandle, HANDLE hThread) {
    /* rewritten from https://github.com/cheat-engine/cheat-engine/blob/master/Cheat%20Engine/CEFuncProc.pas#L3080 */
    DWORD used = 0, ret = 0;
    DWORD stacktop = 0, result = 0;

    MODULEINFO mi;

    GetModuleInformation(processHandle, GetModuleHandle("kernel32.dll"), &mi, sizeof(mi));
    stacktop = (DWORD)GetThreadStackTopAddress_x86(processHandle, hThread);

    /* The stub below has the same result as calling GetThreadStackTopAddress_x86() 
    change line 54 in ntinfo.cpp to return tbi.TebBaseAddress
    Then use this stub
    */
    //LPCVOID tebBaseAddress = GetThreadStackTopAddress_x86(processHandle, hThread);
    //if (tebBaseAddress)
    //  ReadProcessMemory(processHandle, (LPCVOID)((DWORD)tebBaseAddress + 4), &stacktop, 4, NULL);

    CloseHandle(hThread);

    if (stacktop) {
        //find the stack entry pointing to the function that calls "ExitXXXXXThread"
        //Fun thing to note: It's the first entry that points to a address in kernel32

        DWORD* buf32 = new DWORD[4096];

        if (ReadProcessMemory(processHandle, (LPCVOID)(stacktop - 4096), buf32, 4096, NULL)) {
            for (int i = 4096 / 4 - 1; i >= 0; --i) {
                if (buf32[i] >= (DWORD)mi.lpBaseOfDll && buf32[i] <= (DWORD)mi.lpBaseOfDll + mi.SizeOfImage) {
                    result = stacktop - 4096 + i * 4;
                    break;
                }

            }
        }

        delete buf32;
    }

    return result;
}

You can get the thread base addresses in C# like this:

https://stackoverflow.com/a/8737521/1274820

The key is to call the NtQueryInformationThread function. This is not a completely "official" function (possibly undocumented in the past?), but the documentation suggests no alternative for getting the start address of a thread.

I've wrapped it up into a .NET-friendly call that takes a thread ID and returns the start address as IntPtr. This code has been tested in x86 and x64 mode, and in the latter it was tested on both a 32-bit and a 64-bit target process.

One thing I did not test was running this with low privileges; I would expect that this code requires the caller to have the SeDebugPrivilege.

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        PrintProcessThreads(Process.GetCurrentProcess().Id);
        PrintProcessThreads(4156); // some other random process on my system
        Console.WriteLine("Press Enter to exit.");
        Console.ReadLine();
    }

    static void PrintProcessThreads(int processId)
    {
        Console.WriteLine(string.Format("Process Id: {0:X4}", processId));
        var threads = Process.GetProcessById(processId).Threads.OfType<ProcessThread>();
        foreach (var pt in threads)
            Console.WriteLine("  Thread Id: {0:X4}, Start Address: {1:X16}",
                              pt.Id, (ulong) GetThreadStartAddress(pt.Id));
    }

    static IntPtr GetThreadStartAddress(int threadId)
    {
        var hThread = OpenThread(ThreadAccess.QueryInformation, false, threadId);
        if (hThread == IntPtr.Zero)
            throw new Win32Exception();
        var buf = Marshal.AllocHGlobal(IntPtr.Size);
        try
        {
            var result = NtQueryInformationThread(hThread,
                             ThreadInfoClass.ThreadQuerySetWin32StartAddress,
                             buf, IntPtr.Size, IntPtr.Zero);
            if (result != 0)
                throw new Win32Exception(string.Format("NtQueryInformationThread failed; NTSTATUS = {0:X8}", result));
            return Marshal.ReadIntPtr(buf);
        }
        finally
        {
            CloseHandle(hThread);
            Marshal.FreeHGlobal(buf);
        }
    }

    [DllImport("ntdll.dll", SetLastError = true)]
    static extern int NtQueryInformationThread(
        IntPtr threadHandle,
        ThreadInfoClass threadInformationClass,
        IntPtr threadInformation,
        int threadInformationLength,
        IntPtr returnLengthPtr);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, int dwThreadId);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr hObject);

    [Flags]
    public enum ThreadAccess : int
    {
        Terminate = 0x0001,
        SuspendResume = 0x0002,
        GetContext = 0x0008,
        SetContext = 0x0010,
        SetInformation = 0x0020,
        QueryInformation = 0x0040,
        SetThreadToken = 0x0080,
        Impersonate = 0x0100,
        DirectImpersonation = 0x0200
    }

    public enum ThreadInfoClass : int
    {
        ThreadQuerySetWin32StartAddress = 9
    }
}

Output on my system:

Process Id: 2168    (this is a 64-bit process)
  Thread Id: 1C80, Start Address: 0000000001090000
  Thread Id: 210C, Start Address: 000007FEEE8806D4
  Thread Id: 24BC, Start Address: 000007FEEE80A74C
  Thread Id: 12F4, Start Address: 0000000076D2AEC0
Process Id: 103C    (this is a 32-bit process)
  Thread Id: 2510, Start Address: 0000000000FEA253
  Thread Id: 0A0C, Start Address: 0000000076F341F3
  Thread Id: 2438, Start Address: 0000000076F36679
  Thread Id: 2514, Start Address: 0000000000F96CFD
  Thread Id: 2694, Start Address: 00000000025CCCE6

apart from the stuff in parentheses since that requires extra P/Invoke's.


Regarding SymFromAddress "module not found" error, I just wanted to mention that one needs to call SymInitialize with fInvadeProcess = true OR load the module manually, as documented on MSDN.

user1274820
  • 7,786
  • 3
  • 37
  • 74
  • I am doing or at least trying the same thing with the same game. Do you mind to shoot me a mail? Have a look on my profile for contact details. I am a bit stuck since I am using c# only. I have an idea how to use c++ dll with inline asm to read the registers but that seems a bit over the top. Unfortunately I do not understand how you did it with the threadstack. How did you know you needed Thread0 Address. Farmer_First seems to be the struct size. And where did you get the offsets from. Hope you can enlighten me :) – Tom-Oliver Heidel Jun 16 '18 at 01:18
  • I used CheatEngine to find the base address using the pointer scan functionality. There are some tutorials online https://www.unknowncheats.me/forum/programming-for-beginners/110375-cheat-engine-finding-base-address-pointer-scan.html Unfortunately, StardewValley base address pointers change location every time the program is run. I will admit that the pointer I had didn't work 100% of the time. I likely need to do more pointerscans and try to find. More info on this https://www.cheatengine.org/forum/viewtopic.php?p=5185528 – user1274820 Jun 18 '18 at 03:08
  • If you look at the code I used to get that address, you take the ThreadStack0 base address and subtract the Farmer_First address (Jake's screenshot shows what CheatEngine looks like once you've narrowed down the pointer location). The Farmer structure happened to be at that particular offset. Then you read through each offset and the final place in memory you land is the address of the Farmer structure. I'll see if I can get some more screenshots for you to show the process, but the tutorials online likely do a better job. – user1274820 Jun 18 '18 at 03:10
  • Gotta say, @user1274820, your submission is super quality. I especially appreciated where you took the time to convert the threadstack c++ code into c# and didn't just leave it. – Elec0 Jun 15 '20 at 06:31