Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


How to find the right pointer address using C# or C++

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming
View previous topic :: View next topic  
Author Message
d0nk3y
How do I cheat?
Reputation: 0

Joined: 28 Jun 2023
Posts: 6

PostPosted: Wed Jun 28, 2023 5:07 am    Post subject: How to find the right pointer address using C# or C++ Reply with quote

So i found my Pointer address i want to change in CE. It works every time, so i know its the right one.

But when i want to find the pointer value using C# or C++, it seems i cannot find the right one. I also made a pattern search to find the values, but it always find the wrong data.

I have had no issue with this before, but im currently trying to write data to "Spotify", so that i can eventually make a python wrapper in C++. Im just looking to change the volume and skip songs.

But it seems Spotify has some trickery baked in, and i have no idea why i cant find the pointer data im looking for. I allways end up somewhere else, even with the pattern search.


I have tried going thru all the PIDs that Spotify spawns, and searching for both pattern and pointer.. but still nothing. With CE i have it working, but how do i get this to work in C++.

Any suggestions? I have probably written 30 different applications using Memory.dll, CE library, BlackMagic and some other MemSearch libraries, as well as doing just custom code for kernel 32.. Nothing seems to find the same values as CE does.

Anyone knows whats going on with Spotify that i cant seem to grasp?

My current Pointer: ( Volume )
<VariableType>4 Bytes</VariableType>
<Address>"Spotify.exe"+0140D1F0</Address>
<Offsets>
<Offset>2EC</Offset>
<Offset>C</Offset>
<Offset>0</Offset>
<Offset>4</Offset>
<Offset>0</Offset>
<Offset>6D0</Offset>
<Offset>228</Offset>
</Offsets>


Any help would be good.
I don't mind switching libraries or language. I can do C++, C# and Python
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4307

PostPosted: Wed Jun 28, 2023 9:48 am    Post subject: Reply with quote

You're doing something wrong. Maybe you're not derefererncing the pointer nodes along the way, maybe you have the offsets in reverse order, maybe your program doesn't have the correct permissions (do proper error handling), or maybe it's something else entirely.

Post some code if you want better help.

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
d0nk3y
How do I cheat?
Reputation: 0

Joined: 28 Jun 2023
Posts: 6

PostPosted: Wed Jun 28, 2023 4:04 pm    Post subject: Reply with quote

I am, for sure doing something wrong.


So here is one example in C#

Code:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool ReadProcessMemory(
        IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, uint nSize, out int lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool WriteProcessMemory(
        IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);

    static void Main(string[] args)
    {
        int processId = 0;
        string processName = "Spotify";
        Process spotifyProcess = null;

        foreach (Process process in Process.GetProcessesByName(processName))
        {
            spotifyProcess = process;
            break;
        }

        if (spotifyProcess == null)
        {
            Console.WriteLine($"{processName} process not found.");
            return;
        }

        processId = spotifyProcess.Id;
        IntPtr processHandle = NativeMethods.OpenProcess(NativeMethods.PROCESS_VM_READ | NativeMethods.PROCESS_VM_WRITE, false, processId);

        if (processHandle == IntPtr.Zero)
        {
            Console.WriteLine("Failed to open process.");
            return;
        }

        uint baseAddress = 0x0140D1F0; // base address
        int[] offsets = { 0x2EC, 0x0C, 0x00, 0x04, 0x00, 0x6D0, 0x228 }; // Change this to your offsets

        IntPtr currentAddress = new IntPtr(baseAddress);

        foreach (int offset in offsets)
        {
            Console.WriteLine($"Reading memory at address: 0x{currentAddress.ToInt64():X}");

            byte[] buffer = new byte[sizeof(uint)];
            int bytesRead;
            bool success = ReadProcessMemory(processHandle, currentAddress, buffer, sizeof(uint), out bytesRead);

            if (!success || bytesRead != sizeof(uint))
            {
                Console.WriteLine("Failed to read memory.");
                break;
            }

            uint value = BitConverter.ToUInt32(buffer, 0);
            currentAddress = new IntPtr(value + offset);
        }

        // currentAddress now points to the resolved pointer
        byte newValue = 0x90; // value to write ( random for now )

        // Write to memory
        byte[] newValueBytes = { newValue };
        bool writeSuccess = WriteProcessMemory(processHandle, currentAddress, newValueBytes, 1, out int bytesWritten);

        if (writeSuccess && bytesWritten == 1)
        {
            Console.WriteLine("Memory written successfully.");
        }
        else
        {
            Console.WriteLine("Failed to write memory.");
        }

        // Close the process handle
        NativeMethods.CloseHandle(processHandle);
    }
}

static class NativeMethods
{
    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CloseHandle(IntPtr hObject);

    public const uint PROCESS_VM_READ = 0x0010;
    public const uint PROCESS_VM_WRITE = 0x0020;
}



returning:
Reading memory at address: 0x140D1F0
Reading memory at address: 0x2EC
Failed to read memory.
Failed to write memory.

And this in C++:

Code:

#include <iostream>
#include <vector>
#include <Windows.h>
#include <TlHelp32.h>
#include <tchar.h>

BOOL SetDebugPrivilege() {
    HANDLE hToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
        return FALSE;
    }

    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) {
        CloseHandle(hToken);
        return FALSE;
    }

    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
        CloseHandle(hToken);
        return FALSE;
    }

    CloseHandle(hToken);
    return TRUE;
}

DWORD GetSpotifyProcessID() {
    DWORD processID = 0;
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 processEntry;
        processEntry.dwSize = sizeof(processEntry);
        if (Process32First(snapshot, &processEntry)) {
            do {
                if (_tcsicmp(processEntry.szExeFile, _T("spotify.exe")) == 0) {
                    processID = processEntry.th32ProcessID;
                    break;
                }
            } while (Process32Next(snapshot, &processEntry));
        }
        CloseHandle(snapshot);
    }
    return processID;
}

BOOL ReadMemory(HANDLE processHandle, LPCVOID address, LPVOID buffer, SIZE_T size) {
    SIZE_T bytesRead;
    if (ReadProcessMemory(processHandle, address, buffer, size, &bytesRead) == 0) {
        std::cout << "Failed to read process memory. Error code: " << GetLastError() << std::endl;
        return FALSE;
    }
    return (bytesRead == size);
}

int main() {
    if (!SetDebugPrivilege()) {
        std::cout << "Failed to enable debug privilege. Error code: " << GetLastError() << std::endl;
        return 0;
    }

    DWORD processID = GetSpotifyProcessID();
    if (processID == 0) {
        std::cout << "Failed to find Spotify process!" << std::endl;
        return 0;
    }


    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
    if (processHandle == NULL) {
        std::cout << "Failed to open process! Error code: " << GetLastError() << std::endl;
        return 0;
    }

    std::cout << "Process ID: " << processID << std::endl;

    DWORD gameBaseAddr = 0x0140D1F0;  //  base address
    std::cout << "Base Address: 0x" << std::hex << gameBaseAddr << std::endl;

    std::vector<DWORD> offsets = { 0x2EC, 0x0C, 0x00, 0x04, 0x00, 0x6D0, 0x228 };  // offsets

    LPVOID pointerAddress = reinterpret_cast<LPVOID>(gameBaseAddr);
    for (const DWORD offset : offsets) {
        DWORD oldProtection;
        if (!VirtualProtectEx(processHandle, pointerAddress, sizeof(LPVOID), PAGE_EXECUTE_READWRITE, &oldProtection)) {
            std::cout << "Failed to change memory protection. Error code: " << GetLastError() << std::endl;
            CloseHandle(processHandle);
            return 0;
        }

        if (!ReadMemory(processHandle, pointerAddress, &pointerAddress, sizeof(pointerAddress))) {
            std::cout << "Failed to read pointer value!" << std::endl;
            CloseHandle(processHandle);
            return 0;
        }

        pointerAddress = reinterpret_cast<LPVOID>(reinterpret_cast<DWORD_PTR>(pointerAddress) + offset);
    }

    DWORD value;
    if (!ReadMemory(processHandle, pointerAddress, &value, sizeof(value))) {
        std::cout << "Failed to read pointer value!" << std::endl;
    }
    else {
        std::cout << "Value: " << value << std::endl;
    }

    CloseHandle(processHandle);
    return 0;
}



returning:
Process ID: 7920
Base Address: 0x140d1f0
Failed to change memory protection. Error code: 1e7


These are my latest tests, but It seems i can't get access. And when I t looks like I do get access, its like its my own process it tries to read from, or its just me doing something really dumb. (The values are always 0.)


PS: running in administrator, and all local security features on windows is disabled.

thanks.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4307

PostPosted: Wed Jun 28, 2023 9:42 pm    Post subject: Reply with quote

d0nk3y wrote:
Code:
uint baseAddress = 0x0140D1F0; // base address
Is the main module always loaded at the same address? In general you can't make this assumption.
d0nk3y wrote:
Code:
sizeof(uint)
I'm assuming `uint` is 4 bytes. If the process is 64-bit, addresses are 8 bytes. This is probably the problem in your C# code.

d0nk3y wrote:
Code:
reinterpret_cast<LPVOID>(reinterpret_cast<DWORD_PTR>(pointerAddress) + offset);
This is just awful. The biggest error here is probably adding the offset to a pointer to DWORD.
Consider this example:
Code:
#include <cassert>
#include <cstdint>
using std::intptr_t;

int main(int, char**) {
    // array w/ 3 elements
    int a[3] = { 5, 6, 7 };

    int *p0 = &a[0];
    assert(*p0 == 5);
   
    // add 1 to point to the next element
    int *p1 = p0 + 1;
    assert(*p1 == 6);

    // but `int` is 4-bytes:
    static_assert(sizeof(int) == 4);

    // adding 1 to the pointer increased it by 4!
    auto diff = reinterpret_cast<intptr_t>(p1) - reinterpret_cast<intptr_t>(p0);
    assert(diff == 4);

    return 0;
}
C++ is probably the most annoying programming language in the world to get technically correct.

I'm sure other people have made libraries in both C# and C++ for reading pointers in another process. There's probably some tutorials as well.

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 199

Joined: 25 Jan 2006
Posts: 8519
Location: 127.0.0.1

PostPosted: Thu Jun 29, 2023 3:34 am    Post subject: Reply with quote

ParkourPenguin already covered what the main issue likely is, Spotify is most definitely a 64bit application, but in both cases (C#/C++) you are undercasting your pointer to 32bit.

I'll some some other suggestions on your code though:

C#

Code:

        foreach (Process process in Process.GetProcessesByName(processName))
        {
            spotifyProcess = process;
            break;
        }

        if (spotifyProcess == null)
        {
            Console.WriteLine($"{processName} process not found.");
            return;
        }


Rather than do a loop like this, you can make use of C#'s Linq features and directly ask for the first instance of a desired process:

Code:

var processName = "Spotify";
var process = Process.GetProcessesByName(processName).FirstOrDefault();
if (process == null)
{
    Console.WriteLine($"{processName} process not found.");
    return;
}


There's also no need to redefine a variable like you do with 'processId'. You can just directly use it from the process instance when calling OpenProcess. (Also, if you are starting the C# app as admin, you often don't need to call OpenProcess anyway. The process object will return a fully qualified handle itself.)

Another issue, you are only opening your handle with 'NativeMethods.PROCESS_VM_READ | NativeMethods.PROCESS_VM_WRITE', however WriteProcessMemory states in its documentation that you must have PROCESS_VM_OPERATION as well when trying to write to memory. You will want to add that to your flags being used.

You also don't need to create variables to store in other variables like this:

Code:

        // currentAddress now points to the resolved pointer
        byte newValue = 0x90; // value to write ( random for now )

        // Write to memory
        byte[] newValueBytes = { newValue };


You can just directly define the array of data you wish to write:

Code:

var newValueBytes = new byte[] { 0x90 };


For the issues with your types/sizes refer to Parkours post.


C++

It's highly recommended that you default/initialize all your variables before using them as it can lead to unexpected side effects if a function attempts to make use of the variable / value before setting it to something else. Modern C++ compilers allow this to be done in various manners.

In regards to your code, some suggestions:

Code:

// Yours:
HANDLE hToken;

// Initialized:
HANDLE hToken = nullptr;

Code:

// Yours:
TOKEN_PRIVILEGES tp;

// Initialized:
TOKEN_PRIVILEGES tp{};

Code:

// Yours:
SIZE_T bytesRead;
DWORD value;

// Initialized:
SIZE_T bytesRead = 0;
DWORD value = 0;


If you are interested in using some modern C++ such as smart pointers, you can also create auto-closing handles that will automatically call CloseHandle when they go out of scope, like this:

Code:

std::shared_ptr<void> handle(::OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid), ::CloseHandle);
if (handle.get() == INVALID_HANDLE_VALUE)
    return 0;

// Then using the handle in a call such as:
::WriteProcessMemory(handle.get(), ...);


Your C++ code is also suffering from the same pointer size downcasting issue, as you are trying to likely read 64bit pointers but using 32bit variables.

DWORD is a typedef macro/forward for 'unsigned long' which is 4 bytes. Also, be careful with using types that have a mixed size based on the bitness you are compiling in. SIZE_T for example will be dependent on how your application is compiled. In 32bit mode (x86), it's 4 bytes in size. Where as when compiling in 64bit (x64), it will be 8.

With your calls to ReadProcessMemory and WriteProcessMemory, you will want to be using a variable type that is 8 bytes long for your address variables. That would mean instead of DWORD, you'd want to use DWORD_PTR instead. (This type has the same side effect as SIZE_T.)

Your 'pointerAddress' variable can be DWORD_PTR in this case then instead of LPVOID to ensure math being performed on it is happening properly as well. Then when passing it to your memory reader, just pass it via casting.

Here's a bit more of a clean and modern example of using C++ to read memory on a 64bit process. (Requires C++17 or newer.)

Code:

#include <Windows.h>
#include <memory>
#include <string>
#include <TlHelp32.h>

uint32_t get_process_id(const char* name)
{
    const std::shared_ptr<void> handle(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), ::CloseHandle);
    if (handle.get() == INVALID_HANDLE_VALUE)
        return 0;

    PROCESSENTRY32 pe32{sizeof(PROCESSENTRY32), 0};
    if (!::Process32First(handle.get(), &pe32))
        return 0;

    do
    {
        if (!::_strnicmp(pe32.szExeFile, name, ::strlen(name)))
            return pe32.th32ProcessID;
    } while (::Process32Next(handle.get(), &pe32));

    return 0;
}

int32_t __cdecl main(int32_t, char**)
{
    const auto pid = get_process_id("cheatengine-x86_64");
    if (pid == 0)
    {
        std::printf("[!] Failed to locate desired process!\n");
        return 0;
    }

    const std::shared_ptr<void> handle(::OpenProcess(PROCESS_VM_READ | PROCESS_VM_OPERATION, FALSE, pid), ::CloseHandle);

    uint64_t address = 0x7FFDDF3F0000;
    uint64_t value   = 0;

    if (SIZE_T read = 0; !::ReadProcessMemory(handle.get(), reinterpret_cast<LPVOID>(address), &value, sizeof(value), &read) || read != sizeof(value))
    {
        std::printf("Invalid read attempt.\n");
        return 0;
    }

    std::printf("Value: %llu\n", value);
    return 0;
}

_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
d0nk3y
How do I cheat?
Reputation: 0

Joined: 28 Jun 2023
Posts: 6

PostPosted: Sun Jul 02, 2023 4:48 pm    Post subject: Reply with quote

Thanks for the updates. I have updated the C# to x64.

Code:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool ReadProcessMemory(
        IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, uint nSize, out int lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool WriteProcessMemory(
        IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);

    static void Main(string[] args)
    {
        string processName = "Spotify";
        Process spotifyProcess = Process.GetProcessesByName(processName)[0];

        if (spotifyProcess == null)
        {
            Console.WriteLine($"{processName} process not found.");
            return;
        }

        IntPtr processHandle = NativeMethods.OpenProcess(NativeMethods.PROCESS_VM_READ | NativeMethods.PROCESS_VM_WRITE, false, spotifyProcess.Id);

        if (processHandle == IntPtr.Zero)
        {
            Console.WriteLine("Failed to open process.");
            return;
        }

        IntPtr cheatEngineBaseOffset = (IntPtr)0x0140D1F0;
        IntPtr baseAddress = IntPtr.Add(spotifyProcess.MainModule.BaseAddress, cheatEngineBaseOffset.ToInt32());

        Console.WriteLine($"Spotify.exe base address: 0x{spotifyProcess.MainModule.BaseAddress.ToInt64():X8}");
        Console.WriteLine($"Calculated dynamic base address: 0x{baseAddress.ToInt64():X8}");

        IntPtr[] offsets = { new IntPtr(0x228), new IntPtr(0x6D0), new IntPtr(0x0), new IntPtr(0x4), new IntPtr(0x0), new IntPtr(0xC), new IntPtr(0x2EC) }; // Array of offsets to apply

        bool is64bit = Is64Bit(spotifyProcess);
        IntPtr currentAddress = baseAddress;

        // Get the address that baseAddress points to
        byte[] buffer = new byte[is64bit ? 8 : 4];
        bool success = ReadProcessMemory(processHandle, currentAddress, buffer, (uint)buffer.Length, out int bytesRead);

        Console.WriteLine($"Bytes read: {bytesRead}");
        Console.WriteLine($"Bytes: {buffer}");

        if (!success || bytesRead != buffer.Length)
        {
            Console.WriteLine("Failed to read memory.");
            return;
        }

        Console.Write("Bytes read: ");
        for (int i = 0; i < buffer.Length; i++)
        {
            Console.Write($"{buffer[i]:X2} ");
        }
        Console.WriteLine();

        currentAddress = new IntPtr(is64bit ? BitConverter.ToInt64(buffer, 0) : BitConverter.ToInt32(buffer, 0));

        Console.WriteLine($"Address pointed to by base address: 0x{currentAddress.ToInt64():X8}");

        // Add all offsets to current address
        for (int i = 0; i < offsets.Length; i++)
        {
            IntPtr offset = offsets[i];
            Console.WriteLine($"Adding offset 0x{offset.ToInt64():X8} to current address 0x{currentAddress.ToInt64():X8}");
            currentAddress = IntPtr.Add(currentAddress, offset.ToInt32());
            Console.WriteLine($"Address after adding offset: 0x{currentAddress.ToInt64():X8}");
        }

        Console.WriteLine($"Final address after all offsets: 0x{currentAddress.ToInt64():X8}");

        // currentAddress now points to the resolved pointer
        byte newValue = 0x90; // value to write ( random for now )

        // Write to memory
        byte[] newValueBytes = { newValue };
        bool writeSuccess = WriteProcessMemory(processHandle, currentAddress, newValueBytes, 1, out int bytesWritten);

        if (writeSuccess && bytesWritten == 1)
        {
            Console.WriteLine("Memory written successfully.");
        }
        else
        {
            Console.WriteLine("Failed to write memory.");
        }

        // Close process handle
        NativeMethods.CloseHandle(processHandle);
    }

    static bool Is64Bit(Process process)
    {
        if (!Environment.Is64BitOperatingSystem)
            return false;

        bool isWow64;
        NativeMethods.IsWow64Process(process.Handle, out isWow64);
        return Environment.Is64BitOperatingSystem && !isWow64;
    }
}

public static class NativeMethods
{
    public const int PROCESS_VM_READ = 0x0010;
    public const int PROCESS_VM_WRITE = 0x0020;

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

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

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool IsWow64Process([In] IntPtr hProcess, [Out, MarshalAs(UnmanagedType.Bool)] out bool isWow64);
}


And what im doing is:

Spotify.exe base address: 0x00400000
Calculated dynamic base address: 0x0180D1F0
Bytes read: 4
Bytes read: 00 00 00 00
Address pointed to by base address: 0x00000000
Adding offset 0x00000228 to current address 0x00000000
Address after adding offset: 0x00000228
Adding offset 0x000006D0 to current address 0x00000228
Address after adding offset: 0x000008F8
Adding offset 0x00000000 to current address 0x000008F8
Address after adding offset: 0x000008F8
Adding offset 0x00000004 to current address 0x000008F8
Address after adding offset: 0x000008FC
Adding offset 0x00000000 to current address 0x000008FC
Address after adding offset: 0x000008FC
Adding offset 0x0000000C to current address 0x000008FC
Address after adding offset: 0x00000908
Adding offset 0x000002EC to current address 0x00000908
Address after adding offset: 0x00000BF4
Final address after all offsets: 0x00000BF4
Failed to write memory.

So, What i now did was adding: "Calculated dynamic base address: 0x0180D1F0" ( 0x0180D1F0 ) To Cheat Engine, and choose "POINTER".
It now points in Cheat Engine to 05C25A68 .. and adding the offsets to this does work, in cheat engine.

( Pointer.png )

But my code does never resolve 05C25A68 from the correct base address: 0x0180D1F0

So adding offsets to 0x00000000 wont do anything obviously.
So what am i doing wrong. How does Cheat Engine resolve
This "05C25A68" from just knowing 0x0180D1F0?

What am I missing here?



pointer.png
 Description:
 Filesize:  14.55 KB
 Viewed:  5011 Time(s)

pointer.png


Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4307

PostPosted: Sun Jul 02, 2023 7:25 pm    Post subject: Reply with quote

d0nk3y wrote:
Bytes read: 4
Again, this should probably be 8.
Check for errors in `IsWow64Process` w/ GetLastError. Maybe the handle doesn't have the correct access rights.

d0nk3y wrote:
Code:
// Add all offsets to current address
for (int i = 0; i < offsets.Length; i++)
{
    IntPtr offset = offsets[i];
    Console.WriteLine($"Adding offset 0x{offset.ToInt64():X8} to current address 0x{currentAddress.ToInt64():X8}");
    currentAddress = IntPtr.Add(currentAddress, offset.ToInt32());
    Console.WriteLine($"Address after adding offset: 0x{currentAddress.ToInt64():X8}");
}

Console.WriteLine($"Final address after all offsets: 0x{currentAddress.ToInt64():X8}");

First thing I guessed:
ParkourPenguin wrote:
Maybe you're not derefererncing the pointer nodes along the way
If you look at cheat engine, the arrow `->` means "this pointer points to...", or in general "the value stored at this address is..."

Also, your C# app needs to be 64-bit, but that's probably the default

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
d0nk3y
How do I cheat?
Reputation: 0

Joined: 28 Jun 2023
Posts: 6

PostPosted: Sun Jul 02, 2023 8:50 pm    Post subject: Reply with quote

Thanks for the follow up! I did find the issue i was having, ending up with 00 00 00 00 as the pointer.. and it was one of the subprocesses in Spotify having the memory, not the main «Spotify.exe» i found the values now, and the offsets work as expected. Ill update the code here when i finish it, so others can learn from my mistakes. Thanks again for great help and input!
Back to top
View user's profile Send private message
d0nk3y
How do I cheat?
Reputation: 0

Joined: 28 Jun 2023
Posts: 6

PostPosted: Mon Jul 03, 2023 5:28 am    Post subject: Reply with quote

I checked all Spotify instances, to see if it has a value that is not 00 00 00 00
when calculated from the base address. Like so:

Spotify.exe base address: 0x00400000
Calculated dynamic base address: 0x0180D1F0
Bytes read: 8
Bytes: 00-00-00-00-00-00-00-00
Bytes read: 00 00 00 00 00 00 00 00
Skipping process due to zero bytes read.

And when I get any data here, it goes like this:

Spotify.exe base address: 0x00400000
Calculated dynamic base address: 0x0180D1F0
Bytes read: 8
Bytes: 68-5A-C2-05-00-00-00-00
Bytes read: 68 5A C2 05 00 00 00 00
Address after adding offset: 0x05C25C90
Address after adding offset: 0x05C2636C
Address after adding offset: 0x1981CEA8
Address after adding offset: 0x197C9D1C
Address after adding offset: 0x1BC34438
Address after adding offset: 0x090CA6F4
Address after adding offset: 0x1D7EF67C
Final address after all offsets: 0x1D7EF67C
Bytes read from final address:
7D D3 A6 3B
As 4-byte int: 1000788861
As float: 0,005091129
WriteProcessMemory result: True
Successfully wrote 1060502150 to address 0x1D7EF67C


My code now looks like this. This is basically just code to confirm that I can read and write to the desired memory, so I do apologize for the way its structured, but hopefully it can help someone in a similar situation.

Code:


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

class Program
{
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool ReadProcessMemory(
        IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, uint nSize, out int lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool WriteProcessMemory(
        IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool VirtualProtectEx(
        IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

    static void Main(string[] args)
    {
        string processName = "Spotify";
        Process[] spotifyProcesses = Process.GetProcessesByName(processName);

        foreach (Process spotifyProcess in spotifyProcesses)
        {

            IntPtr processHandle = NativeMethods.OpenProcess(NativeMethods.PROCESS_ALL_ACCESS | NativeMethods.PROCESS_VM_WRITE, false, spotifyProcess.Id);

            if (processHandle == IntPtr.Zero)
            {
                Console.WriteLine($"Failed to open process for {spotifyProcess.ProcessName}.");
                continue;
            }

            IntPtr cheatEngineBaseOffset = (IntPtr)0x0140D1F0;
            IntPtr baseAddress = IntPtr.Add(spotifyProcess.MainModule.BaseAddress, cheatEngineBaseOffset.ToInt32());

            Console.WriteLine($"Spotify.exe base address: 0x{spotifyProcess.MainModule.BaseAddress.ToInt64():X8}");
            Console.WriteLine($"Calculated dynamic base address: 0x{baseAddress.ToInt64():X8}");

            int[] offsets = { 0x228, 0x6D0, 0x0, 0x4, 0x0, 0xC, 0x2EC }; // Array of offsets to apply

            bool is64bit = Is64Bit(spotifyProcess);
            IntPtr currentAddress = baseAddress;

            // Always allocate an 8-byte buffer
            byte[] buffer = new byte[8];
            bool success = ReadProcessMemory(processHandle, currentAddress, buffer, (uint)buffer.Length, out int bytesRead);

            Console.WriteLine($"Bytes read: {bytesRead}");
            Console.WriteLine($"Bytes: {BitConverter.ToString(buffer)}");

            if (!success || bytesRead != buffer.Length)
            {
                Console.WriteLine("Failed to read memory.");
                NativeMethods.CloseHandle(processHandle);
                continue;
            }

            Console.Write("Bytes read: ");
            for (int i = 0; i < buffer.Length; i++)
            {
                Console.Write($"{buffer[i]:X2} ");
            }
            Console.WriteLine();

            if (BitConverter.ToInt32(buffer, 0) != 0x00000000)
            {
                long addressValue = is64bit ? BitConverter.ToInt64(buffer, 0) : BitConverter.ToInt32(buffer, 0);

                for (int i = 0; i < offsets.Length; i++)
                {
                    currentAddress = new IntPtr(addressValue + offsets[i]); // Add the offset to the value read from memory
                    success = ReadProcessMemory(processHandle, currentAddress, buffer, (uint)buffer.Length, out bytesRead);

                    if (!success || bytesRead != buffer.Length)
                    {
                        Console.WriteLine("Failed to read memory.");
                        NativeMethods.CloseHandle(processHandle);
                        break;
                    }

                    addressValue = is64bit ? BitConverter.ToInt64(buffer, 0) : BitConverter.ToInt32(buffer, 0);
                    Console.WriteLine($"Address after adding offset: 0x{currentAddress.ToInt64():X8}");
                }

                Console.WriteLine($"Final address after all offsets: 0x{currentAddress.ToInt64():X8}");

                // Read 4 bytes from the final address
                byte[] finalAddressBytes = new byte[4];
                success = ReadProcessMemory(processHandle, currentAddress, finalAddressBytes, (uint)finalAddressBytes.Length, out bytesRead);

                if (success && bytesRead == finalAddressBytes.Length)
                {
                    Console.WriteLine("Bytes read from final address:");
                    for (int i = 0; i < finalAddressBytes.Length; i++)
                    {
                        Console.Write($"{finalAddressBytes[i]:X2} ");
                    }
                    Console.WriteLine();
                    int intResult = BitConverter.ToInt32(finalAddressBytes, 0);
                    float floatResult = BitConverter.ToSingle(finalAddressBytes, 0);
                    Console.WriteLine($"As 4-byte int: {intResult}");
                    Console.WriteLine($"As float: {floatResult}");

                    int valueToWrite = 1060502150;
                    byte[] bytesToWrite = BitConverter.GetBytes(valueToWrite);

                    uint oldProtection;
                    bool protectSuccess = VirtualProtectEx(
                        processHandle, currentAddress, (UIntPtr)bytesToWrite.Length, 0x04, out oldProtection);

                    if (protectSuccess)
                    {
                        bool writeSuccess = WriteProcessMemory(
                            processHandle, currentAddress, bytesToWrite, (uint)bytesToWrite.Length, out int bytesWritten);

                        VirtualProtectEx(
                            processHandle, currentAddress, (UIntPtr)bytesToWrite.Length, oldProtection, out _);

                        if (writeSuccess && bytesWritten == bytesToWrite.Length)
                        {
                            Console.WriteLine($"WriteProcessMemory result: {writeSuccess}");
                            Console.WriteLine($"Successfully wrote {valueToWrite} to address 0x{currentAddress.ToInt64():X8}");
                        }
                        else
                        {
                            Console.WriteLine($"WriteProcessMemory result: {writeSuccess}");
                            Console.WriteLine("Failed to write to memory.");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Failed to change memory protection.");
                    }
                }
                else
                {
                    Console.WriteLine("Failed to read memory from the final address.");
                }

                // Close process handle
                NativeMethods.CloseHandle(processHandle);
            }
            else
            {
                Console.WriteLine("Skipping process due to zero bytes read.");
                NativeMethods.CloseHandle(processHandle);
                continue;
            }
        }
    }

    static bool Is64Bit(Process process)
    {
        if (!Environment.Is64BitOperatingSystem)
            return false;

        bool isWow64;
        NativeMethods.IsWow64Process(process.Handle, out isWow64);
        return Environment.Is64BitOperatingSystem && !isWow64;
    }
}

public static class NativeMethods
{
    public const int PROCESS_VM_READ = 0x0010;
    public const int PROCESS_VM_WRITE = 0x0020;
    public const int PROCESS_ALL_ACCESS = 0x1F0FFF;

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

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

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool IsWow64Process([In] IntPtr hProcess, [Out, MarshalAs(UnmanagedType.Bool)] out bool isWow64);
}



And again, Thank you so much for the great help pointing me in the right direction guys. Really
Back to top
View user's profile Send private message
d0nk3y
How do I cheat?
Reputation: 0

Joined: 28 Jun 2023
Posts: 6

PostPosted: Wed Jul 05, 2023 4:55 am    Post subject: Reply with quote

Following up with a working C++ version as well, for those who might need it:

Code:

#include <iostream>
#include <windows.h>
#include <vector>
#include <tlhelp32.h>
#include <string>


bool Is64Bit(HANDLE process)
{
    BOOL isWow64;
    IsWow64Process(process, &isWow64);
    return !(BOOL)isWow64;
}

int main()
{
    DWORD processID;
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32W entry;
    entry.dwSize = sizeof entry;
    std::wstring processName = L"Spotify.exe";

    if (!Process32FirstW(snap, &entry))
    {
        return false;
    }

    do
    {
        if (std::wstring(entry.szExeFile) == processName)
        {
            processID = entry.th32ProcessID;
            break;
        }
    } while (Process32NextW(snap, &entry));

    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, processID);

    if (processHandle == NULL)
    {
        std::wcout << L"Failed to open process for " << processName << std::endl;
        return 1;
    }

    uintptr_t cheatEngineBaseOffset = 0x0140D1F0;

    MODULEENTRY32W modEntry;
    HANDLE snapMod = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);
    modEntry.dwSize = sizeof(modEntry);

    uintptr_t baseAddress;
    if (Module32FirstW(snapMod, &modEntry))
    {
        baseAddress = (uintptr_t)modEntry.modBaseAddr;
    }

    uintptr_t dynamicBaseAddress = baseAddress + cheatEngineBaseOffset;

    std::wcout << L"Spotify.exe base address: 0x" << std::hex << baseAddress << std::endl;
    std::wcout << L"Calculated dynamic base address: 0x" << std::hex << dynamicBaseAddress << std::endl;

    std::vector<int> offsets = { 0x228, 0x6D0, 0x0, 0x4, 0x0, 0xC, 0x2EC }; // Array of offsets to apply

    bool is64bit = Is64Bit(processHandle);
    uintptr_t currentAddress = dynamicBaseAddress;

    SIZE_T bytesRead;
    unsigned long long buffer;
    ReadProcessMemory(processHandle, (LPVOID)currentAddress, &buffer, sizeof(buffer), &bytesRead);

    std::wcout << L"Bytes read: " << bytesRead << std::endl;
    std::wcout << L"Bytes: " << std::hex << buffer << std::endl;

    if (bytesRead != sizeof(buffer))
    {
        std::wcout << L"Failed to read memory." << std::endl;
        CloseHandle(processHandle);
        return 1;
    }

    if (buffer != 0x00000000)
    {
        uintptr_t addressValue = buffer;
        for (int i = 0; i < offsets.size(); i++)
        {
            currentAddress = addressValue + offsets[i]; // Add the offset to the value read from memory
            ReadProcessMemory(processHandle, (LPVOID)currentAddress, &buffer, sizeof(buffer), &bytesRead);

            if (bytesRead != sizeof(buffer))
            {
                std::wcout << L"Failed to read memory." << std::endl;
                CloseHandle(processHandle);
                return 1;
            }

            addressValue = buffer;
            std::wcout << L"Address after adding offset: 0x" << std::hex << currentAddress << std::endl;
        }

        std::wcout << L"Final address after all offsets: 0x" << std::hex << currentAddress << std::endl;

        // Read 4 bytes from the final address
        unsigned long finalAddressValue;
        ReadProcessMemory(processHandle, (LPVOID)currentAddress, &finalAddressValue, sizeof(finalAddressValue), &bytesRead);

        if (bytesRead == sizeof(finalAddressValue))
        {
            std::wcout << L"Bytes read from final address: " << std::hex << finalAddressValue << std::endl;
            std::wcout << L"As 4-byte int: " << finalAddressValue << std::endl;
            std::wcout << L"As float: " << *(float*)&finalAddressValue << std::endl;

            int valueToWrite = 1060502150;
            WriteProcessMemory(processHandle, (LPVOID)currentAddress, &valueToWrite, sizeof(valueToWrite), &bytesRead);

            if (bytesRead == sizeof(valueToWrite))
            {
                std::wcout << L"Successfully wrote " << valueToWrite << " to address 0x" << std::hex << currentAddress << std::endl;
            }
            else
            {
                std::wcout << L"Failed to write to memory." << std::endl;
            }
        }
        else
        {
            std::wcout << L"Failed to read memory from the final address." << std::endl;
        }

        CloseHandle(processHandle);
    }
    else
    {
        std::wcout << L"Skipping process due to zero bytes read." << std::endl;
        CloseHandle(processHandle);
    }

    return 0;
}
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites