Memory Process reading/Writing & Pattern Scans (Array of bytes)
Posted: 28 Dec 2013, 03:02
Let's help each other out
https://www.autohotkey.com/boards/
Code: Select all
explorerExe := new memory("ahk_exe explorer.exe")
msgbox % explorerExe.BaseAddress
Code: Select all
#Include <classMemory>
if (_ClassMemory.__Class != "_ClassMemory")
{
msgbox class memory not correctly installed.
ExitApp
}
explorerExe := new _ClassMemory("ahk_exe explorer.exe")
SetFormat, IntegerFast, H ; View the addresses in hex
msgbox % explorerExe.BaseAddress ; This works for me on explorer. For some applications it will not be correct. getmodulebaseAddress() always seems to work. (but its bitness dependant)
. "`n" explorerExe.getmodulebaseAddress() ; fails if AHK is 32 and target is 64 bit. When ahk is 64 bit this will work with both 64 and 32 bit target apps
Code: Select all
stringAdress := vlc.processPatternScan( ,, 0x30, 0x30, 0x3a, 0x30, 0x34)
Code: Select all
bbb := 0x30, 0x30, 0x3a, 0x30, 0x34
stringAdress := vlc.processPatternScan( ,, bbb)
Code: Select all
bbb := 0x30303a3034
stringAdress := vlc.processPatternScan( ,, bbb)
Code: Select all
AOB := stringToAOBPattern("00:04", "UTF-8")
stringAdress := vlc.processPatternScan( ,, AOB*)
stringToAOBPattern(string, encoding := "UTF-8", insertNullTerminator := False)
{
AOBPattern := []
encodingSize := (encoding = "utf-16" || encoding = "cp1200") ? 2 : 1
requiredSize := StrPut(string, encoding) * encodingSize - (insertNullTerminator ? 0 : encodingSize)
VarSetCapacity(buffer, requiredSize)
StrPut(string, &buffer, StrLen(string) + (insertNullTerminator ? 1 : 0), encoding)
loop, % requiredSize
AOBPattern.Insert(NumGet(buffer, A_Index-1, "UChar"))
return AOBPattern
}
Code: Select all
pattern := hexStrToAOBPattern("30303a3034")
; hexString is a string of hex bytes (2-digits) without the '0x' hex prefix.
; eg.
; DEADBEEF
; A byte can be denoted wild by using two question marks (or any other character that isn't a hex number)
; DEAD??EF - the third byte is wild
hexStrToAOBPattern(hexString)
{
AOBPattern := []
length := StrLen(hexString)
if !length || Mod(length, 2)
return -1 ; no str or string is not an even number of characters - 2 characters per byte
loop, % length/2
{
value := "0x" SubStr(hexString, 1 + 2 * (A_index-1), 2)
if (value + 0 = "")
value := "?"
AOBPattern.Insert(value)
}
return AOBPattern
}
Code: Select all
AOB := hexToAOBPattern(0xA734dFFF345643C)
patternAdress := vlc.processPatternScan( ,, AOB*)
Code: Select all
#include classMemory.ahk
vlc := new _ClassMemory("ahk_exe vlc.exe", "", hProcessCopy)
stringAdress := vlc.processPatternScan( ,, 0x30, 0x30, 0x3a, 0x30, 0x34)
SetFormat, IntegerFast, hex
stringAdress += 0 ; Sets Var (which previously contained 11) to be 0xb.
stringAdress .= "" ; Necessary due to the "fast" mode.
SetFormat, IntegerFast, d
MsgBox, % stringAdress
pattern := hexStrToAOBPattern("30303a3034")
SetFormat, IntegerFast, hex
pattern += 0 ; Sets Var (which previously contained 11) to be 0xb.
pattern .= "" ; Necessary due to the "fast" mode.
SetFormat, IntegerFast, d
MsgBox, % pattern
; hexString is a string of hex bytes (2-digits) without the '0x' hex prefix.
; eg.
; DEADBEEF
; A byte can be denoted wild by using two question marks (or any other character that isn't a hex number)
; DEAD??EF - the third byte is wild
hexStrToAOBPattern(hexString)
{
AOBPattern := []
length := StrLen(hexString)
if !length || Mod(length, 2)
return -1 ; no str or string is not an even number of characters - 2 characters per byte
loop, % length/2
{
value := "0x" SubStr(hexString, 1 + 2 * (A_index-1), 2)
if (value + 0 = "")
value := "?"
AOBPattern.Insert(value)
}
return AOBPattern
}
; AOB is object!
AOB := stringToAOBPattern("00:04", "UTF-8")
stringAdress2 := vlc.processPatternScan( ,, AOB*)
stringToAOBPattern(string, encoding := "UTF-8", insertNullTerminator := False)
{
AOBPattern := []
encodingSize := (encoding = "utf-16" || encoding = "cp1200") ? 2 : 1
requiredSize := StrPut(string, encoding) * encodingSize - (insertNullTerminator ? 0 : encodingSize)
VarSetCapacity(buffer, requiredSize)
StrPut(string, &buffer, StrLen(string) + (insertNullTerminator ? 1 : 0), encoding)
loop, % requiredSize
AOBPattern.Insert(NumGet(buffer, A_Index-1, "UChar"))
return AOBPattern
}
SetFormat, IntegerFast, hex
stringAdress2 += 0 ; Sets Var (which previously contained 11) to be 0xb.
stringAdress2 .= "" ; Necessary due to the "fast" mode.
SetFormat, IntegerFast, d
MsgBox, % stringAdress2
Code: Select all
pattern := hexStrToAOBPattern("30303a3034")
SetFormat, IntegerFast, hex
pattern += 0 ; Sets Var (which previously contained 11) to be 0xb.
pattern .= "" ; Necessary due to the "fast" mode.
SetFormat, IntegerFast, d
MsgBox, % pattern
Code: Select all
pattern := hexStrToAOBPattern("30303a3034")
stringAdress3 := vlc.processPatternScan( ,, pattern*)
Code: Select all
; Initial call to get the size needed
DllCall("psapi\EnumProcessModulesEx", "ptr", hProcess, "ptr", 0, "uint", 0, "uint*", size, "uint", 0x03)
; Allocate space for use with DllCall
cb := VarSetCapacity(hModule, size, 0)
; Second call to get the data we want
DllCall("psapi\EnumProcessModulesEx", "ptr", hProcess, "ptr", &hModule, "uint", cb, "uint*", size, "uint", 0x03)
Code: Select all
#Include classMemory.ahk
#SingleInstance Force
TargetProcess := new _ClassMemory("ahk_exe notepad.exe", "", hProcessCopy)
myAOBstringpattern := "Thisisatest"
ConvertBase(InputBase, OutputBase, number)
{
static u := A_IsUnicode ? "_wcstoui64" : "_strtoui64"
static v := A_IsUnicode ? "_i64tow" : "_i64toa"
VarSetCapacity(s, 65, 0)
value := DllCall("msvcrt.dll\" u, "Str", number, "UInt", 0, "UInt", InputBase, "CDECL Int64")
DllCall("msvcrt.dll\" v, "Int64", value, "Str", s, "UInt", OutputBase, "CDECL")
return s
}
Loop
{
myAOBscan := TargetProcess.stringToPattern(myAOBstringpattern, "UTF-16")
myAOBaddressdec := TargetProcess.processPatternScan(,, myAOBscan*)
if (myAOBaddressdec > 0) ;AOB found, stop scanning and convert it from decimal to hexadecimal
{
myAOBaddresshex := "0x" ConvertBase(10, 16, myAOBaddressdec)
MsgBox, found AOB at: %myAOBaddresshex%
ExitApp
}
else ;AOB not found, continue scanning
{
Sleep,500
}
}
When you mention read(), I assume you're refering to some ReadMemory function in another library.... replacing the AOB scan in your code with a _classMemory.read() definitely wont work.I can't seem to be able to loop stringToPattern() searches while the targeted program does not exist yet. I can do that easily with read()
Notes:
If the target process exits and then starts again (or restarts) you will need to free the derived object and then use the new operator to create a new object i.e.
calc := [] ; or calc := "" ; free the object. This is actually optional if using the line below, as the line below would free the previous derived object calc prior to initialising the new copy.
calc := new _ClassMemory("ahk_exe calc.exe") ; Create a new derived object to read calc's memory.
Code: Select all
#Include classMemory.ahk
#SingleInstance Force
#Persistent
process, wait, notepad.exe ; wait indefinitely for process to exist
settimer, exist, 100 ; when process exits close the script
TargetProcess := new _ClassMemory("ahk_exe notepad.exe", "", hProcessCopy)
myAOBstringpattern := "Thisisatest"
Loop
{
myAOBscan := TargetProcess.stringToPattern(myAOBstringpattern, "UTF-16")
; My notepad is 64 bits, so need to increase the endAddress parameter to find this pattern
myAOBaddress := TargetProcess.processPatternScan(, TargetProcess.isTarget64bit ? 0x7FFFFFFFFFF : 0x7FFFFFFF, myAOBscan*)
if (myAOBaddress > 0)
MsgBox, found AOB at: %myAOBaddress%
else
Sleep,500
}
return
; when process exits close the script
exist:
if !WinExist("ahk_exe notepad.exe")
ExitApp
; alternatively
;Process, exist, notepad.exe
;if !ErrorLevel
; ExitApp
return
Code: Select all
#Include classMemory.ahk
#SingleInstance Force
#Persistent
settimer, setMemory, 50
return
doScans:
myAOBscan := TargetProcess.stringToPattern("Thisisatest", "UTF-16")
myAOBaddress := TargetProcess.processPatternScan(, TargetProcess.isTarget64bit ? 0x7FFFFFFFFFF : 0x7FFFFFFF, myAOBscan*)
if (myAOBaddress > 0)
{
SetFormat, IntegerFast, H
myAOBaddress += 0
myAOBaddress .= ""
SetFormat, IntegerFast, D
msgbox patterns found at %myAOBaddress%
}
return
setMemory:
; if TargetProcess hasn;t been set and the process exists - open a handle
if !IsObject(TargetProcess) && WinExist("ahk_exe notepad.exe")
{
TargetProcess := new _ClassMemory("ahk_exe notepad.exe", "", hProcessCopy)
SetTimer, doScans, 100 ; enable all memory reading timers
}
else if !WinExist("ahk_exe notepad.exe")
{
settimer, doScans, off ; disable all memory reading timers
TargetProcess := "" ; destroy the memory object
}
return
Code: Select all
#Include classMemory.ahk
#SingleInstance Force
#Persistent
settimer, setMemory, 50
msgbox This requires ClassMemory version 2.8 or greater!
return
doScans:
myAOBscan := TargetProcess.stringToPattern("Thisisatest", "UTF-16")
myAOBaddress := TargetProcess.processPatternScan(,, myAOBscan*)
if (myAOBaddress > 0)
{
SetFormat, IntegerFast, H
myAOBaddress += 0
myAOBaddress .= ""
SetFormat, IntegerFast, D
msgbox patterns found at %myAOBaddress%
}
return
setMemory:
; If new _ClassMemory() has never been called then TargetProcess.isHandleValid() will return null (as TargetProcess isn't an object yet)
; If new _ClassMemory() has been successfully called, then isHandleValid() will return false after notepad closes or restarts
; otherwise it returns true
if TargetProcess.isHandleValid()
return
else if WinExist("ahk_exe notepad.exe")
{
TargetProcess := new _ClassMemory("ahk_exe notepad.exe", "", hProcessCopy)
SetTimer, doScans, 100 ; enable all memory reading timers
}
else ; handle is invalid and target process doesn't exist
{
settimer, doScans, off ; disable all memory reading timers
}
return
That is a valid point and I did consider it when writing the functions. I elected not to for the simple fact that the vast majority of times that these functions are called they will not be outputting values for people to read. When debugging, its easy to place the thread into hex mode via setformat. And when outputting found addresses for some other purpose you will often want it in a specific format - perhaps with or without the '0x' prefix and/or left zero padded which would could negate any default conversion.I think classMemory should convert the found AOB address from decimal to hexadecimal AUTOMATICALLY.
None of the functions in this class convert dec to hex or vice versa.I mean the rest of the stuff I worked with in classMemory outputted addresses in hex
No I mean the read method described in your library. classMemory is the only library I've ever used for memory related stuff in AHK. From your documentation:RHCP wrote:When you mention read(), I assume you're refering to some ReadMemory function in another library.... replacing the AOB scan in your code with a _classMemory.read() definitely wont work.
Code: Select all
Commonly used methods:
read()
yeah my bad I meant every time I use read() and write() I feed it addresses written in hex not dec like this:None of the functions in this class convert dec to hex or vice versa.
Code: Select all
#Include classMemory.ahk
;request admin rights
full_command_line := DllCall("GetCommandLine", "str")
if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
{
try
{
if A_IsCompiled
RunWait *RunAs "%A_ScriptFullPath%" /restart
else
RunWait *RunAs "%A_AhkPath%" /restart "%A_ScriptFullPath%"
}
}
if not A_IsAdmin
{
MsgBox, Administrator rights not found. The program might not work.
}
;SeDebugPrivilege just in case
_ClassMemory.setSeDebugPrivilege()
$*F1::
TargetProcess := new _ClassMemory("ahk_exe notepad.exe", "", hProcessCopy)
TargetProcess.suspend()
return
$*F2::
TargetProcess := new _ClassMemory("ahk_exe notepad.exe", "", hProcessCopy)
TargetProcess.resume()
return
Code: Select all
;request admin rights
full_command_line := DllCall("GetCommandLine", "str")
if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
{
try
{
if A_IsCompiled
RunWait *RunAs "%A_ScriptFullPath%" /restart
else
RunWait *RunAs "%A_AhkPath%" /restart "%A_ScriptFullPath%"
}
}
if not A_IsAdmin
{
MsgBox, Administrator rights not found. The program might not work.
}
;enable SeDebugPrivilege just in case
Process, Exist ; sets ErrorLevel to the PID of this running script
; Get the handle of this script with PROCESS_QUERY_INFORMATION (0x0400)
h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "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")
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
;suspension-related functions
SuspendProcess(pid) {
hProcess := DllCall("OpenProcess", "UInt", 0x1F0FFF, "Int", 0, "Int", pid)
If (hProcess) {
DllCall("ntdll.dll\NtSuspendProcess", "Int", hProcess)
DllCall("CloseHandle", "Int", hProcess)
}
}
ResumeProcess(pid) {
hProcess := DllCall("OpenProcess", "UInt", 0x1F0FFF, "Int", 0, "Int", pid)
If (hProcess) {
DllCall("ntdll.dll\NtResumeProcess", "Int", hProcess)
DllCall("CloseHandle", "Int", hProcess)
}
}
ProcessIsSuspended(pid, ByRef isPartiallySuspended := 0) {
static initialBufferSize := 0x4000, cbSYSTEM_THREAD_INFORMATION := A_PtrSize == 8 ? 80 : 64
static SystemProcessInformation := 5, STATUS_BUFFER_TOO_SMALL := 0xC0000023, STATUS_INFO_LENGTH_MISMATCH := 0xC0000004
static Waiting := 5, Suspended := 5
static UniqueProcessIdOffset := A_PtrSize == 8 ? 80 : 68, NumberOfThreadsOffset := 4, ThreadsArrayOffset := A_PtrSize == 8 ? 256 : 184
static ThreadStateOffset := A_PtrSize == 8 ? 68 : 52, WaitReasonOffset := A_PtrSize == 8 ? 72 : 56
bufferSize := initialBufferSize
VarSetCapacity(ProcessBuffer, bufferSize)
Loop {
status := DllCall("ntdll\NtQuerySystemInformation", "UInt", SystemProcessInformation, "Ptr", &ProcessBuffer, "UInt", bufferSize, "UInt*", bufferSize, "UInt")
if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) {
VarSetCapacity(ProcessBuffer, bufferSize)
}
else {
break
}
}
if (status < 0) {
return False
}
if (bufferSize <= 0x100000) initialBufferSize := bufferSize
isSuspended := pid > 0
isPartiallySuspended := False
ThisEntryOffset := 0
Loop {
if (NumGet(ProcessBuffer, ThisEntryOffset + UniqueProcessIdOffset, "Ptr") == pid) {
Loop % NumGet(ProcessBuffer, ThisEntryOffset + NumberOfThreadsOffset, "UInt") {
ThisThreadsOffset := ThisEntryOffset + ThreadsArrayOffset + (cbSYSTEM_THREAD_INFORMATION * (A_Index - 1))
ThreadState := NumGet(ProcessBuffer, ThisThreadsOffset + ThreadStateOffset, "UInt")
WaitReason := NumGet(ProcessBuffer, ThisThreadsOffset + WaitReasonOffset, "UInt")
if (ThreadState != Waiting || WaitReason != Suspended) {
isSuspended := False
} else {
isPartiallySuspended := True
}
}
return isSuspended
}
} until (!(NextEntryOffset := NumGet(ProcessBuffer, ThisEntryOffset, "UInt")), ThisEntryOffset += NextEntryOffset)
return -1
}
$*F1::
Process, Exist, notepad.exe
pid := ErrorLevel
if !ProcessIsSuspended(pid)
{
SuspendProcess(pid)
}
else
{
ResumeProcess(pid)
}
return
Code: Select all
;request admin rights
full_command_line := DllCall("GetCommandLine", "str")
if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
{
try
{
if A_IsCompiled
RunWait *RunAs "%A_ScriptFullPath%" /restart
else
RunWait *RunAs "%A_AhkPath%" /restart "%A_ScriptFullPath%"
}
}
if not A_IsAdmin
{
MsgBox, Administrator rights not found. The program might not work.
}
;enable SeDebugPrivilege just in case
Process, Exist ; sets ErrorLevel to the PID of this running script
; Get the handle of this script with PROCESS_QUERY_INFORMATION (0x0400)
h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "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")
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
$*F1::
Process, Exist, notepad.exe
pid := ErrorLevel
DllCall("DebugActiveProcess", "UInt", pid)
return
$*F2::
Process, Exist, notepad.exe
pid := ErrorLevel
DllCall("DebugActiveProcessStop", "UInt", pid)
return
What is this supposed to do, besides change the English text to look like Japanese characters instead? UFT-8 I understand as English, and reads perfectly for what I'm trying to accomplish.RHCP wrote: ↑28 Jun 2019, 00:18The first string is unicode.
Try:Code: Select all
Exile := poe.readString(0x7FFB69C3AE78,, encoding := "UTF-16")
Code: Select all
Hotbar:= XIV.hexStringtoPattern("48 4F 54 42 41 52 2E 44 41 54 00 00 01 00 00 00 70")