Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

ReadMemory function


  • Please log in to reply
109 replies to this topic
Wizbaggd
  • Members
  • 15 posts
  • Last active: Jan 18 2014 03:09 AM
  • Joined: 18 Sep 2013

OK, One step much much closer.

 

FADDRESS returns the value of the item at the address.

 

Did the suggested method with the direct address from CE, that worked with your code perfectly, now I just need to have it work with 2 offsets and this thing will be working!



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

OK, One step much much closer.

 

FADDRESS returns the value of the item at the address.

 

 

 

What do you mean be returns?

if you go msgbox % FADDRESS 

is that the value of the item? As it should be the address of the item.



Wizbaggd
  • Members
  • 15 posts
  • Last active: Jan 18 2014 03:09 AM
  • Joined: 18 Sep 2013

MsgBox %FADDRESS%

 

returns the current value of the variable stored at the memory address.



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

MsgBox %FADDRESS%

 

returns the current value of the variable stored at the memory address.

 

You need to simply add/+ the last offset, not perform a readmemory on it. 

I.e

FADDRESS := ReadMemory(ReadMemory(getProcessBaseAddress(PROGRAM) + MADDRESS, PROGRAM) + Offset_1, PROGRAM) + Offset_2

Here is a function based on yours which can write to an address with as many offsets as you wish - or none at all.

WriteMemory(program, value, address, size := 4, aOffsets*)
{
    winget, pid, PID, %PROGRAM%
   	address += getProcessBaseAddress(program)
   	if aOffsets.maxIndex()
   	{
   		lastOffset := aOffsets.Remove() ;remove the highest key so can use pointer() to find final memory address (minus the last offset)
		address := pointer(program, address, aOffsets*) + lastOffset 
	}

    ProcessHandle := DllCall("OpenProcess", "int", 2035711, "Int", 0, "UInt", PID, "UInt")
    DllCall("WriteProcessMemory", "UInt", ProcessHandle, "UInt", address, "ptr*", value, "Uint", size, "Uint*", bytesWritten)
    DllCall("CloseHandle", "int", ProcessHandle)
	return bytesWritten
}

Pointer function

; Can pass an array of offsets by using *
; eg, pointer(game, base, [0x10, 0x30, 0xFF]*)
; or a := [0x10, 0x30, 0xFF]
; pointer(game, base, a*)
; or just type them in manually

pointer(game, base, offsets*)
{ 
	For index, offset in offsets
	{
		if (index = offsets.maxIndex() && A_index = 1)
			pointer := offset + ReadMemory(base, game)
		Else 
		{
			IF (A_Index = 1) 
				pointer := ReadMemory(offset + ReadMemory(base, game), game)
			Else If (index = offsets.MaxIndex())
				pointer += offset
			Else pointer := ReadMemory(pointer + offset, game)
		}
	}	
	Return ReadMemory(offsets.maxIndex() ? pointer : base, game)
}

Also this version of getBaseAddress() will work with 64bit versions of AHK:

; 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
    }
    WinGet, hWnd, ID, %WindowTitle%
    if mode
        SetTitleMatchMode, %mode%    ; In case executed in autoexec
    if !hWnd
        return ; return blank failed to find window
    return DllCall(A_PtrSize = 4     ; If DLL call fails, returned value will = 0
        ? "GetWindowLong"
        : "GetWindowLongPtr"
        , "Ptr", hWnd, "Int", -6, A_Is64bitOS ? "Int64" : "UInt")  
        ; For the returned value when the OS is 64 bit use Int64 to prevent negative overflow when AHK is 32 bit and target process is 64bit 
        ; however if the OS is 32 bit, must use UInt, otherwise the number will be huge (however it will still work as the lower 4 bytes are correct)      
        ; Note - it's the OS bitness which matters here, not the scripts/AHKs
}    

Im a bit bored, so I might write a memory class which wraps all the read/write memory functions into one thing and includes strings as well.

 

Cheers.

 

Edit: Fixed an error in the pointer/write memory function.



Wizbaggd
  • Members
  • 15 posts
  • Last active: Jan 18 2014 03:09 AM
  • Joined: 18 Sep 2013

Man... you are a legend!

 

I knew I was overlooking something small, that Read was the issue. Thank you for the neater functions!

 

 

Much appreciation! Happy Holidays!



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

Edit: Updated this class 09/08/14

 

Glad it works for you. I too made a simple error in the writememory function i posted above, it's easy to do with pointers! Previously it would write to the wrong address if a pointer with one offset was used. It's fixed now.
 
 
Here is a general wrapper/class for the most commonly used memory functions. The various read/write functions support pointers of any length.

 

Includes:

read()

ReadRawMemory()

readString()

writeString()

write()

pointer()

getProcessBaseAddress()

getBaseAddressOfModule()

modulePatternScan()
adressPatternScan()
processPatternScan()
rawPatternScan()
 
There are a few other 'internal' methods which may also be useful. The pattern scan methods accept an array of byte values and supports wildcards. They call a machine code function, so they are quite fast.
 

GitHub: https://github.com/K...classMemory.ahk

 

Other individual memory functions I have posted can also be found in the Lib folder of that GitHub project. These will be updated with any fixes/changes - the ones posted in this thread wont.

 

If anyone finds bugs, let me know and I will update the class.



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

Here is another method to get the process base address. This method iterates the modules. You can also use sizeOfImage to find the last address of the module.

msgbox % getModuleBaseAddress("ahk_exe calc.exe") ; returns the base address of calc.exe
; Parameters:
;   Program - Can be any window title/class e.g "ahk_exe calc.exe"
;   Module - The file name of the module/dll to find e.g. "GDI32.dll" etc
;            If no module is specified, the address of the base module - main() (program) will be returned e.g. C:\Windows\SysWOW64\calc.exe

; Return Values: 
;   Positive integer - Module base address
;   -1 - Module not found
;   -2 - Couldn't find the process. The program isn't running or you passed an incorrect program identifier parameter
;   -3 - Couldn't open the process. If the target process is running with admin rights, then the script will also need to be ran as admin. 
;   -4 - Problem with EnumProcessModules. This shouldn't happen.
;   -5 - The AHK script is 32 bit and you are trying to access the modules of a 64 bit target process.

; Note: A 64 bit AHK can enumerate the modules of a target 64 or 32 bit process.
;       A 32 bit AHK (any process actually) can only enumerate the modules of another 32 bit process

getModuleBaseAddress(program, module := "")
{
    WinGet, pid, pid, %program%
    if pid                              ; PROCESS_QUERY_INFORMATION + PROCESS_VM_READ 
        hProc := DllCall("OpenProcess", "UInt", 0x0400 | 0x0010 , "Int", 0, "UInt", pid)
    else return -2
    if !hProc
        return -3
    if (A_PtrSize = 4) ; AHK 32bit
    {
        DllCall("IsWow64Process", "Ptr", hProc, "Int*", result)
        if !result 
            return -5, DllCall("CloseHandle","Ptr",hProc)  ; AHK is 32bit and target process is 64 bit, this function wont work 
    }
    if (module = "")
    {        
        VarSetCapacity(mainExeNameBuffer, 2048 * (A_IsUnicode ? 2 : 1))
        DllCall("psapi\GetModuleFileNameEx", "Ptr", hProc, "UInt", 0
                    , "Ptr", &mainExeNameBuffer, "UInt", 2048 / (A_IsUnicode ? 2 : 1))
        mainExeFullPath := StrGet(&mainExeNameBuffer)
        ; mainExeName = main executable module of the process (will include full directory path)
    }
    size := VarSetCapacity(lphModule, 4)
    loop 
    {
        DllCall("psapi\EnumProcessModules", "Ptr", hProc, "Ptr", &lphModule
                , "UInt", size, "UInt*", reqSize)
        if ErrorLevel
            return -4, DllCall("CloseHandle","Ptr",hProc) 
        else if (size >= reqSize)
            break
        else 
            size := VarSetCapacity(lphModule, reqSize)    
    }
    VarSetCapacity(lpFilename, 2048 * (A_IsUnicode ? 2 : 1))
    loop % reqSize / A_PtrSize ; sizeof(HMODULE) - enumerate the array of HMODULEs
    {
        DllCall("psapi\GetModuleFileNameEx", "Ptr", hProc, "Ptr", numget(lphModule, (A_index - 1) * A_PtrSize)
                , "Ptr", &lpFilename, "UInt", 2048 / (A_IsUnicode ? 2 : 1))
        ; module will contain directory path as well e.g C:\Windows\syswow65\GDI32.dll
        moduleFullPath := StrGet(&lpFilename) 
        SplitPath, moduleFullPath, fileName ; strips the path so = GDI32.dll
        if (module = "" && mainExeFullPath = moduleFullPath) || (module != "" && module = filename)
        {
            VarSetCapacity(MODULEINFO, A_PtrSize = 4 ? 12 : 24)
            DllCall("psapi\GetModuleInformation", "Ptr", hProc, "Ptr", numget(lphModule, (A_index - 1) * A_PtrSize)
                , "Ptr", &MODULEINFO, "UInt", A_PtrSize = 4 ? 12 : 24)
            return numget(MODULEINFO, 0, "Ptr"), DllCall("CloseHandle","Ptr",hProc)
        }
    }
    return -1, DllCall("CloseHandle","Ptr",hProc) ; not found
}


kalik
  • Members
  • 53 posts
  • Last active: Sep 04 2015 07:56 AM
  • Joined: 09 Jun 2012
EDIT: Sorry, moved to support.

icecub
  • Members
  • 6 posts
  • Last active: Sep 24 2014 11:43 PM
  • Joined: 29 Jul 2014

@RHCP: I've been trying to work with your library but I don't fully understand it. I'm trying to get a value out of Java based game. Obviously the process is attached to "javaw.exe" and the module would be "jvm.dll". My base pointer looks like this:

 

""jvm.dll"+00338E84" with offsets (top to bottom): 0x8, 0x294, 0x4b8, 0x24, 0x20

 

My code so far is:

#include %a_scriptdir%/classMemory.ahk

java := new _ClassMemory("ahk_exe javaw.exe", "", hProcessCopy)

if !isObject(java)
    msgbox failed to open a handle

arrayPointerOffsets := [0x20, 0x24, 0x4B8, 0x294, 0x8]
value := java.read("jvm.dll"+00338E84, "UInt", arrayPointerOffsets*)

msgbox %value%

Obviously this is wrong. But I have no idea on how to make it correct. Could you please help me with this one? All I need is being able to read out the value. Nothing else.



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

A couple of days ago I changed the names of a couple of methods in the class. 

 

In the updated version you need to call getModuleBaseAddress() to get the address of "jvm.dll".  In the older version this method was called getBaseAddressOfModule(). Im not sure which version you are using, if the method fails check the class code to see which one is correct.

 

Also, when passing the address "00338E84" you need to use the hex prefix so AHK knows it is a hexidecimal number and not a string.

 

Does this work?

#include %a_scriptdir%/classMemory.ahk

java := new _ClassMemory("ahk_exe javaw.exe")
if !isObject(java)
    msgbox failed to open a handle
; A couple of days ago I have changed the method/function name on github 
; Current version 1.3 on github has this method
baseAddress := java.getModuleBaseAddress("jvm.dll")
msgbox Is baseAddress: %baseAddress%  The same as in cheat engine?`nMake sure to convert to hex if required.
; The old method name was getBaseAddressOfModule()
arrayPointerOffsets := [0x20, 0x24, 0x4B8, 0x294, 0x8]
value := java.read(baseAddress + 0x00338E84, "UInt", arrayPointerOffsets*)

msgbox %value%


icecub
  • Members
  • 6 posts
  • Last active: Sep 24 2014 11:43 PM
  • Joined: 29 Jul 2014

Yes that's working perfectly fine! Thanks a lot for your help!



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

Cool, just out of curiosity what is the bitness of the java application - 64/32bit ?

 

I wrote the library with 32 bit apps in mind (it's somewhat rare for a game to be 64 bit). If the target game is 64 bit then some of the methods like getBaseAddressOfModule() will only work if the AHK exe is also 64 bit (Windows limitation). Also, some of the pointer functions may not work with very high addresses, I will try to fix this.



icecub
  • Members
  • 6 posts
  • Last active: Sep 24 2014 11:43 PM
  • Joined: 29 Jul 2014

The application is written in 32 bit. It's a simple game which I'm using to learn writing trainers. I thought it'd be funny to use AHK instead of something like VB or C++ / C# to do this. Also because with your library it's a whole lot easier for newbies to write their own trainers.

 

I noticed a problem with Java though. If you install both java 32 bit and 64 bit and the JDK, any apps based on java will switch between all the JVMs on each new launch. Causing the game to have different pointers thanks to the switching. Like:

 

1st launch: Set 1 pointers
2nd launch: Set 2 pointers
3rd launch: Set 1 pointer again
4th launch: Set 2 pointers again

 

Took me a while to figure that out xD



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

Thanks for the tip.

 

I've made some changes and the various read/write functions now completely support pointers in 64 bit target applications.  The only caveat is that the AHK exe must also be 64 bit.  If AHK is 32 bit and the target application is 64 bit you can still read, write, and use pointers, so long as the addresses can fit into a 4 byte ptr - the pBaseAddress parameter in readProcessMemory() is the limiting factor here.

 

I'll update the git class in a day or two.



icecub
  • Members
  • 6 posts
  • Last active: Sep 24 2014 11:43 PM
  • Joined: 29 Jul 2014

That's great! Although I hardly think you'll ever run into an address that needs a 8 byte ptr. Maybe somewhere in the future when gamemakers are comfortable enough that all their customers have a 64 bit operating system. For now it's just easier to write games into 32 bit format. There's an exception when you're messing around with something else than games ofcourse. But personally I've never had a reason to mess with ram values from anything but games.

 

Anyway. Going to watch a movie and of to bed for me. I look forward to the update.