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
evan
  • Guests
  • Last active:
  • Joined: --
Its not my first time using ReadProcessMemory functions to read 4byte value in a game client at certain memory address, but i always have difficulties copy and paste this and that... declaring the functions
so today, i made up my mind and wrap it up together into a library function

Here:
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,"UInt",MADDRESS,"Str",MVALUE,"UInt",4,"UInt *",0)

Loop 4
result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)

return, result  
}
(put this in "C:\Program Files\AutoHotkey\lib" folder with "ReadMemory.ahk" as name)

example usage:
Run calc.exe
winwait, Calc
StartTime := A_TickCount

loop 1000
value:=ReadMemory(0x41000C,"Calc")

ElapsedTime := A_TickCount - StartTime
msgbox, Memory address 0x41000C = %value%`nTake %ElapsedTime% ms to loop 1000 times

Posted Image

this function will refresh PID and process every time
but as shown in the results, ~5000 times per second isnt bad at all, because usually we only need like 1-2 times per second in reading a game client value

welcome for any suggestions
i really appreciated if i can improve this, as i use memory address often

evan
  • Guests
  • Last active:
  • Joined: --
of course, i do have the write memory address function too:
WriteMemory(WVALUE,MADDRESS,PROGRAM)
{
winget, pid, PID, %PROGRAM%

ProcessHandle := DllCall("OpenProcess", "int", 2035711, "char", 0, "UInt", PID, "UInt")
DllCall("WriteProcessMemory", "UInt", ProcessHandle, "UInt", MADDRESS, "Uint*", WVALUE, "Uint", 4, "Uint *", 0)

DllCall("CloseHandle", "int", ProcessHandle)
return
}

(put this in "C:\Program Files\AutoHotkey\lib" folder with "WriteMemory.ahk" as name)

example usage:
WriteMemory(10, 0x41000C,"Calc")
;(InputValue, Address, Program)


Ice_Tea
  • Members
  • 131 posts
  • Last active: Aug 25 2010 11:11 AM
  • Joined: 12 Jan 2008
Hey

Well you should make 4 separate functions.
OpenHandle
ReadMemory
WriteMemory
CloseHandle

You're putting 3 functions into 1, which causes the whole thing to slow down.

Posted Image
As you see, it's way more efficient.

evan
  • Guests
  • Last active:
  • Joined: --
can u show me the script u using to produce that result?
i assume it is:
OpenHandle
loop1000, ReadMemory
CloseHandle

i appreciate the suggestions, but i am aiming for all-in-one
if not all-in-one, why in the first place i need to make a function library
each for the 4 functions is just 1 line anyways

VxE
  • Moderators
  • 3622 posts
  • Last active: Dec 24 2015 02:21 AM
  • Joined: 07 Oct 2006
If you still want to have it 'all-in-one', then you could have a static variable in the function to hold the ProcessHandle, then use a special flag (in the processname parameter) to know when to close the handle.

Also, it might be nice to have the option for users who want to read chars one at a time, not 4 at a time ;)

(also, for the readmemory function, the first param of the OpenProcess call only needs to be 16. Similarly, using PROCESS_ALL_ACCESS for the writememory function is definitely overkill)

evan
  • Guests
  • Last active:
  • Joined: --
o i see what u meant
but i dont quite get how it can be done to check should the program open/close handle from a library function
actually i dont really know what it meant by close, as it shows no difference with/without it
so lets say if i have a settimer every 1 second to check my current HP in a game client, the program will press certain key if HP low, or winKill the client when its 0
how should my open, read, close function approach?
usually i will just open the process from the beginning of my script and keep reading it until program exitapp

Ice_Tea
  • Members
  • 131 posts
  • Last active: Aug 25 2010 11:11 AM
  • Joined: 12 Jan 2008
Hmm, Evan I'd like to write the function for you but atm I'm busy with another project...

It seems like you haven't understood that you can put multiple functions into 1 "library", meaning that you could put Openprocess,read/write-memory,Closeprocess into 1 single file...

Well the way you should build the script is like this:
;*When starting the script*
OpenProcess ;*Make sure that ur process exists*
OnExist, Close ;goes to the label "close" when the script is terminated
Settimer, updateHP, 1000
return

updateHP:
ReadMemory
return

Close:
CloseProcess
Return

btw I might clean it up for you later...Sorry about not being able to share that script, as you see it was run as "~acc.tmp" it's a command console for AHK (it's made by Titan), it only saves 1 script and I've already overwritten it... well all I did was stripping your function, putting openprocess outside, and close outside, leaving readmemory alone in the function.

Btw about closing the handle, I dunno if that's necessary, the whole thing works fine without it... but just incase, it would be good to unload it when you shutdown the script, it's just another additional line...

evan
  • Guests
  • Last active:
  • Joined: --
if i use this in library, it doesnt work
i guess because the ProcessHandle can not work outside the function so it wont react with ReadMemory()

OpenProcess(PROGRAM)
{
winget, pid, PID, %PROGRAM%
ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
}

ReadMemory(MADDRESS)
{
VarSetCapacity(MVALUE,4,0)

DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",4,"UInt *",0)

Loop 4
result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)

return, result 
}


ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006
you need to set ProcessHandle as a global var in each func it is used.
Posted Image
ʞɔпɟ əɥʇ ʇɐɥʍ

Ice_Tea
  • Members
  • 131 posts
  • Last active: Aug 25 2010 11:11 AM
  • Joined: 12 Jan 2008
I'm on my way to bed, so I'll have to keep this short:
as ahklerner said, just put Processhandle as a global variable:

Global ProcessHandle := DLLcallBLABLABLABLA
(Not sure if you can declear a variable as global like that, I think that it should work)

Regarding the library with many functions, you need to give them specific index for the variables or simply "#include LIBRARYNAME.ahk"... and it should solve your problems.

nitenite
*sry about the poor explanation*

evan
  • Guests
  • Last active:
  • Joined: --
thanks for the suggestion on globe var
i come out with this:
ReadMemory(MADDRESS,PROGRAM)
{
static Count = 0
    
if Count = 0
{
winget, pid, PID, %PROGRAM%
global  ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
Count = 1 
}

VarSetCapacity(MVALUE,4,0)
DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",4,"UInt *",0)

Loop 4
result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)

return, result  
}

Run time is 3 times faster than before - 92ms
still all-in-one, but will only winget 1 time and openprocess 1 time
the 60ms difference than yours, i assume it is the check if statement 1000 times

Ice_Tea
  • Members
  • 131 posts
  • Last active: Aug 25 2010 11:11 AM
  • Joined: 12 Jan 2008
Nope a if statement shouldn't affect a lot at all... but the difference could be in our CPUs... a weaker CPU would give worse results...

VxE
  • Moderators
  • 3622 posts
  • Last active: Dec 24 2015 02:21 AM
  • Joined: 07 Oct 2006
Here's my cleanup of your ReadMemory() function:

Perks:
- Obtains the PID and handle to a process once for any number of calls to the same PROGRAM.
- Does not loop to obtain the 32bit int (increases speed marginally)
- Some error handling ("Fail" for failure to read or open process, and "Handle Closed: ?" when just closing the handle)
- My laptop clocked it at 26ms for 1000 calls using SetBatchLines, -1

This function should execute at a speed on par with simply looping ReadProcessMemory between OpenProcess and CloseHandle.
; Automatically closes handle when a new (or null) program is indicated
; Otherwise, keeps the process handle open between calls that specify the
; same program. When finished reading memory, call this function with no
; parameters to close the process handle i.e: "Closed := ReadMemory()"
ReadMemory(MADDRESS=0,PROGRAM="")
{
	Static OLDPROC, ProcessHandle
	VarSetCapacity(MVALUE,4,0)
	If PROGRAM != %OLDPROC%
	{
		WinGet, pid, pid, % OLDPROC := PROGRAM
		ProcessHandle := ( ProcessHandle ? 0*(closed:=DllCall("CloseHandle"
		,"UInt",ProcessHandle)) : 0 )+(pid ? DllCall("OpenProcess"
		,"Int",16,"Int",0,"UInt",pid) : 0)
	}
	If (ProcessHandle) && DllCall("ReadProcessMemory","UInt"
	,ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",4,"UInt *",0)
	return *(&MVALUE+3)<<24 | *(&MVALUE+2)<<16 | *(&MVALUE+1)<<8 | *(&MVALUE)
	return !ProcessHandle ? "Handle Closed: " closed : "Fail"
}


evan
  • Guests
  • Last active:
  • Joined: --
excellent
i like the idea that include some error checking

i am glad that i share my script and your comments/suggestions will benefit my future memory reading projects. thanks

Deicer
  • Members
  • 43 posts
  • Last active: Aug 18 2011 12:11 PM
  • Joined: 22 Dec 2006
Quick question.

When I use ReadMemory on a adress value eg. 2635 it returns 2635 (ok)
When I use ReadMemory on a adress value eg. HELLO it returns 1919595761 (Wrong)

How to convert/fix this, so it returns HELLO ?

ReadMemory looks like this:

ReadMemory(MADDRESS=0,PROGRAM="")
{
   Static OLDPROC, ProcessHandle
   VarSetCapacity(MVALUE,4,0)
   If PROGRAM != %OLDPROC%
   {
      WinGet, pid, pid, % OLDPROC := PROGRAM
      ProcessHandle := ( ProcessHandle ? 0*(closed:=DllCall("CloseHandle"
      ,"UInt",ProcessHandle)) : 0 )+(pid ? DllCall("OpenProcess"
      ,"Int",16,"Int",0,"UInt",pid) : 0)
   }
   If (ProcessHandle) && DllCall("ReadProcessMemory","UInt"
   ,ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",4,"UInt *",0)
   return *(&MVALUE+3)<<24 | *(&MVALUE+2)<<16 | *(&MVALUE+1)<<8 | *(&MVALUE)
   return !ProcessHandle ? "Handle Closed: " closed : "Fail"
}

My code:

value:=ReadMemory(0x02F3F4F4,"SomeApp.exe") 
MsgBox %value%

Thanks