getProcessBaseAddress

Ask gaming related questions (AHK v1.1 and older)
mmartin1212
Posts: 10
Joined: 26 Jul 2015, 03:21

getProcessBaseAddress

26 Jul 2015, 03:26

I have tried both using the class library and just pulling the functions.
Here is my code at the moment.

Code: Select all

#Include Classmemory.ahk
#SingleInstance force
; Return values:
;   Null        The process's window couldn't be found.
;   0           The GetWindowLong or GetWindowLongPtr call failed.
;   Non-Zero    The base address of the process (success).

getProcessBaseAddress(WindowTitle, windowMatchMode := "3")    ;WindowTitle can be anything ahk_exe ahk_class etc
{
    if (windowMatchMode && A_TitleMatchMode != windowMatchMode)
    {
        mode := A_TitleMatchMode ; This is a string and will not contain the 0x prefix
        StringReplace, windowMatchMode, windowMatchMode, 0x ; remove hex prefix as SetTitleMatchMode will throw a run time error. This will occur if integer mode is set to hex and matchmode param is passed as an number not a string.
        SetTitleMatchMode, %windowMatchMode%    ;mode 3 is an exact match
    }
    else windowMatchMode := False
    WinGet, hWnd, ID, %WindowTitle%
    if windowMatchMode
        SetTitleMatchMode, %mode%    ; In case executed in autoexec
    if !hWnd
        return ; return blank failed to find window
    else return DllCall(  A_PtrSize = 4
                        ? "GetWindowLong"
                        : "GetWindowLongPtr"
                        , "Ptr", hWnd
                        , "Uint", -6
                        , "Int64") ; Use Int64 to prevent negative overflow when AHK is 32 bit and target process is 64bit
     ; If DLL call fails, returned value will = 0
}	

ReadMemory(MADDRESS,PROGRAM)
{
winget, pid, PID, %PROGRAM%

VarSetCapacity(MVALUE,4,0)
ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
DllCall("ReadProcessMemory", "UInt", ProcessHandle, "Ptr", MADDRESS, "Ptr", &MVALUE, "Uint",4)
Loop 4
result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)

return, result
}
;------------------------------------------------------------
if !A_IsAdmin 
{
    try  Run *RunAs "%A_ScriptFullPath%"
    catch
        msgbox Please Run this again with admin rights.
    ExitApp
}


GameIdentifier := ahk_exe Game.exe
BaseAddress := getProcessBaseAddress("Diablo II")
pointer10 := BaseAddress + 0x10502C
address10 := ReadMemory(Pointer10, "Diablo II")
pointer0 := Address10 + 0x14
address1 := ReadMemory(Pointer0, "Diablo II")
pointer1 := address1 + 0x3ac
address2 := ReadMemory(Pointer1, "Diablo II")
pointer2 := address2 + 0x24
address3 := ReadMemory(Pointer2, "Diablo II")
ArrowP := address3 + 0x4
Arrow := ReadMemory(arrowP, "Diablo II")

msgbox %BaseAddress% 
msgbox %Address10%
return


/*
if (_ClassMemory.__Class != "_ClassMemory")
    msgbox class memory not correctly installed. Or the (global class) variable "_ClassMemory" has been overwritten
 
 
mem := new _ClassMemory("ahk_exe Game.exe", "", hProcessCopy) 
if !isObject(mem)
    msgbox failed to open a handle
if !hProcessCopy
    msgbox failed to open a handle. Error Code = %hProcessCopy%
 
msgbox % mem.BaseAddress
gamesPlayed := mem.read(mem.BaseAddress + 0x10502C, "UInt", 0x14, 0x3ac, 0x24, 0x4)
msgbox % gamesPlayed
*/
The part that is uncommented out is the section from which BaseAddress Returns the value of 4194304.
Do I need to convert this into Hex? This is just a test project so I can utilize memory reading and data management in my other scripts. I really want to understand this as step by step as possible.

Thank you.
RHCP
Posts: 202
Joined: 30 Sep 2013, 10:59

Re: getProcessBaseAddress

26 Jul 2015, 04:24

What's the issue?

You do not need to convert a value between decimal and hex or vice versa - the functions dont care if it is passed as a hex value or a decimal one. In AHK hex values are denoted by the prefix '0x'

The decimal value 4194304 is represented as 0x400000 in hex. This is the default base address for win32 executables - excluding ones which use ASLR (dynamic base address).

Your ReadMemory() function is opening a new handle but fails to close it. The DLLCall is also missing the final parameter. With modern versions of AHK the bitwise operations are no longer required, the result can be directly obtained via the DLLCall.
e.g.

Code: Select all

ReadMemoryUInt(MADDRESS,PROGRAM)
{
    winget, pid, PID, %PROGRAM%
    ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
    DllCall("ReadProcessMemory", "Ptr", ProcessHandle, "Ptr", MADDRESS, "UInt*", result, "Uint", 4, "Ptr", 0)
    DllCall("CloseHandle", "Ptr", ProcessHandle)
    return result
}
This line will result in a blank variable.

Code: Select all

GameIdentifier := ahk_exe Game.exe
The line should be:

Code: Select all

GameIdentifier := "ahk_exe Game.exe"
; or
GameIdentifier = ahk_exe Game.exe
mmartin1212
Posts: 10
Joined: 26 Jul 2015, 03:21

Re: getProcessBaseAddress

26 Jul 2015, 04:37

Okay So i changed the read memory to as you have said.

Code: Select all

BaseAddress := getProcessBaseAddress("Diablo II")
pointer10 := BaseAddress + 0x1050D8
address10 := ReadMemoryUint(Pointer10, "Diablo II")
pointer0 := Address10 + 0x5c
address1 := ReadMemoryUint(Pointer0, "Diablo II")
pointer1 := address1 + 0x24
address2 := ReadMemoryUint(Pointer1, "Diablo II")
pointer2 := address2 + 0x4
address3 := ReadMemoryUint(Pointer2, "Diablo II")

msgbox %BaseAddress% 
msgbox %Address10%
return
For some reason my pointer that was working over the course of 10 close and reopens stopped working. So I found a new one. Base address pulled through. Address1-3 all read 0. Did I format it wrong? How can I double check what is happening?


Thank you so much for answering.
RHCP
Posts: 202
Joined: 30 Sep 2013, 10:59

Re: getProcessBaseAddress

26 Jul 2015, 04:58

The function will not work with AHK basic or extremely old versions of AHK_L - "Ptr" wasn't a valid dllCall type back then.
Check to ensure the function is working correctly. Try reading a valid address without the pointers and check that returned value is correct.
If it's working fine, then check the result of each ReadMemoryUInt call in your code to find where it's failing - e.g. it's trying to read a value from an unreadable region of memory.


Code: Select all

ReadMemoryUInt(MADDRESS,PROGRAM)
{
    winget, pid, PID, %PROGRAM%
    ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
    resultRPM := DllCall("ReadProcessMemory", "Ptr", ProcessHandle, "Ptr", MADDRESS, "UInt*", result, "Uint", 4, "Ptr", 0)
    DllCall("CloseHandle", "Ptr", ProcessHandle)
  	
  	msgbox % "handle: "   ProcessHandle
  		. "`nresultRPM: " resultRPM
  		. "`nresult: " result
  		. "`nversion: " A_AhkVersion

    return result
}
Edit:
Error in function. (it wouldn't cause the issues you're experiencing though)
Forgot to replace UChar* with UInt* after testing the function on a uchar value.
Last edited by RHCP on 26 Jul 2015, 09:15, edited 1 time in total.
mmartin1212
Posts: 10
Joined: 26 Jul 2015, 03:21

Re: getProcessBaseAddress

26 Jul 2015, 05:22

Code: Select all

BaseAddress := getProcessBaseAddress("Diablo II")
pointer2 := BaseAddress + 0x3bE1B2C
address3 := ReadMemoryUint(pointer2, "Diablo II")
msgbox %address3%
msgbox %BaseAddress%
So I added what you said. It fails directly after Base Address. Base address comes up.

The debug lines are

Handle: 0
ResultRPM: 0
Result: 0
Version: 1.1.22.03

I made an error somewhere.

Code: Select all

GameIdentifier := "ahk_exe Maind2.exe"
BaseAddress := getProcessBaseAddress(GameIdentifier)
pointer2 := BaseAddress + 0x3bE1B2C
address3 := ReadMemoryUint(pointer2, GameIdentifier)
msgbox %address3%
msgbox %BaseAddress%
I tried that as well. But it won't even give me base address. Could this be related to running through scite4autohotkey? I have AutoHotkey V1.1.22.03 Unicode 32-bit installed.
RHCP
Posts: 202
Joined: 30 Sep 2013, 10:59

Re: getProcessBaseAddress

26 Jul 2015, 05:51

Handle: 0
ResultRPM: 0
Result: 0
Version: 1.1.22.03
This indicates the problem is either with the OpenProcess call, or winget didn't find the window.
Check the pid value in the function.
If the PID is blank or 0 then it's not finding the window. Ensure that the window title is correct and the script has admin privileges. Perhaps try another window identifier title and/or searching for hidden windows (detecthiddenwindows).
If it's finding the pid, then the OpenProcess call is failing. Checking the value of A_lasterror after DLLCall may reveal what's happening.
mmartin1212
Posts: 10
Joined: 26 Jul 2015, 03:21

Re: getProcessBaseAddress

26 Jul 2015, 06:02

It is finding pid. And the correct one. A_LastError = 6. So if I am reading docs that translates to 0xc0000006? Which is Exception_in_page_error? You probably know better than me
RHCP
Posts: 202
Joined: 30 Sep 2013, 10:59

Re: getProcessBaseAddress

26 Jul 2015, 09:29

The function in my previous post contained an error, however it doesn't explain the issues you're having.
ERROR_INVALID_HANDLE
6 (0x6)
The handle is invalid.
A_lastError is not the same as errorlevel.

I'm not really sure what would cause that. Did you check A_lasterror immediately after the openprocess call? e.g.

Code: Select all

    ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
    msgbox % A_lasterror 
Otherwise that's probably the error message from the readprocessmemory or closeHandle call.


Although I've never encountered it outside of system processes, some programs apparently require the script to have seDebugPrivileges.

Call setSeDebugPrivilege(True) once at the start of the script after you have elevated it to admin.

Code: Select all

setSeDebugPrivilege(enable := True)
{
    h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", DllCall("GetCurrentProcessId"), "Ptr")
    ; Open an adjustable access token with this process (TOKEN_ADJUST_PRIVILEGES = 32)
    DllCall("Advapi32.dll\OpenProcessToken", "Ptr", h, "UInt", 32, "PtrP", t)
    VarSetCapacity(ti, 16, 0)  ; structure of privileges
    NumPut(1, ti, 0, "UInt")  ; one entry in the privileges array...
    ; Retrieves the locally unique identifier of the debug privilege:
    DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", "SeDebugPrivilege", "Int64P", luid)
    NumPut(luid, ti, 4, "Int64")
    if enable
    	NumPut(2, ti, 12, "UInt")  ; enable this privilege: SE_PRIVILEGE_ENABLED = 2
    ; Update the privileges of this process with the new access token:
    r := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", t, "Int", false, "Ptr", &ti, "UInt", 0, "Ptr", 0, "Ptr", 0)
    DllCall("CloseHandle", "Ptr", t)  ; close this access token handle to save memory
    DllCall("CloseHandle", "Ptr", h)  ; close this process handle to save memory
    return r
}

Return to “Gaming Help (v1)”

Who is online

Users browsing this forum: No registered users and 57 guests