Memory Process reading/Writing & Pattern Scans (Array of bytes)
Memory Process reading/Writing & Pattern Scans (Array of bytes)
Last edited by RHCP on 25 Jul 2016, 21:57, edited 4 times in total.
Re: Memory Process reading/Writing
I get total emptiness if I run:
Should this also work for explorer.exe?
Edit: I'm using Windows 10, 64 bit.
I've made a in-memory patch for explorer.exe and tested it using x64dbg, and now I'm thinking best way to apply it without x64dbg. But since I need to also call GetWindowLongPtrW within the explorer.exe process context, I wonder if this is at all possible with AHK, because I'm not sure if I can use CreateRemoteThread from AHK at all, it may require me to write C++ which makes the patch more rigid.
Code: Select all
explorerExe := new memory("ahk_exe explorer.exe")
msgbox % explorerExe.BaseAddress
Edit: I'm using Windows 10, 64 bit.
I've made a in-memory patch for explorer.exe and tested it using x64dbg, and now I'm thinking best way to apply it without x64dbg. But since I need to also call GetWindowLongPtrW within the explorer.exe process context, I wonder if this is at all possible with AHK, because I'm not sure if I can use CreateRemoteThread from AHK at all, it may require me to write C++ which makes the patch more rigid.
Re: Memory Process reading/Writing
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
The description in the original post is outdated.
It's possible to call CreateRemoteThread in AHK and consequently call functions in remote processes. I've done it before, but only as a test.
Re: Memory Process reading/Writing
First of all thanks for great script!
This works fine:
But I need to use instead of aAOBPattern* a variable with hex number. Something like:
OR
How can I do it?
This works fine:
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)
DRAKON-AutoHotkey: Visual programming for AutoHotkey.
Re: Memory Process reading/Writing
There are a few ways, but it depends on what that number represents.
The found address is being stored as 'stringAdress' so this kinda indicates that youre searching for a string i.e. '00:04'.
I'm guessing you're trying to search for a changing string?
A neater method:
The found address is being stored as 'stringAdress' so this kinda indicates that youre searching for a string i.e. '00:04'.
I'm guessing you're trying to search for a changing string?
A neater method:
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
}
Last edited by RHCP on 16 Jan 2016, 13:30, edited 1 time in total.
Re: Memory Process reading/Writing
Thank you It works great with strings.
How to instead of string 00:04 search for some hex pattern. I mean something like this:
How to instead of string 00:04 search for some hex pattern. I mean something like this:
Code: Select all
AOB := hexToAOBPattern(0xA734dFFF345643C)
patternAdress := vlc.processPatternScan( ,, AOB*)
DRAKON-AutoHotkey: Visual programming for AutoHotkey.
Re: Memory Process reading/Writing
I edited the post above, hexStrToAOBPattern() does exactly that. Don't use the hex prefix.
Re: Memory Process reading/Writing
Here is my code:
MsgBox, % stringAdress and MsgBox, % stringAdress2 are giving me correct result. But MsgBox, % pattern gives 0x0 . What I am doing wrong?
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
MsgBox, % stringAdress and MsgBox, % stringAdress2 are giving me correct result. But MsgBox, % pattern gives 0x0 . What I am doing wrong?
DRAKON-AutoHotkey: Visual programming for AutoHotkey.
Re: Memory Process reading/Writing
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
hexStrToAOBPattern() is returning an object, like you did in the other examples, you need to pass that object to a pattern scan method.
Code: Select all
pattern := hexStrToAOBPattern("30303a3034")
stringAdress3 := vlc.processPatternScan( ,, pattern*)
https://autohotkey.com/board/topic/6483 ... ts-easily/
Cheers.
Re: Memory Process reading/Writing
Thanks Works great!
DRAKON-AutoHotkey: Visual programming for AutoHotkey.
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
Really good script.
Thank you very much
Thank you very much
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
I have built 3 game trainer tools around classMemory, thank you RHCP.
For those who would like to have a fully working basic example of the memory read and write functions, take a look at my stuff https://autohotkey.com/boards/viewtopic.php?&t=24155
For those who would like to have a fully working basic example of the memory read and write functions, take a look at my stuff https://autohotkey.com/boards/viewtopic.php?&t=24155
YOU'RE NOT ALEXANDER
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
Suggestion for EnumProcessModulesEx
Instand of loop to get the size you can call it twice:
Instand of loop to get the size you can call it twice:
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)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
Hello RHCP
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() so that my memory tool will pick up the right memory addresses as soon as the targeted program is detected. This way I'm not forcing the user to run the target program BEFORE the memory tool, thus making it easier to use.
Here's an example. If I first launch Notepad, type Thisisatest and launch the script, the AOB is correctly found:Launching Notepad, launching the script and finally typing Thisisatest also works.
But if I first launch the script, then Notepad and type Thisisatest, the AOB will never be found even though the search is looped. Is there any workaround for this? I want to make my scripts as easy as possible for users.
Also, just a suggestion, I think classMemory should convert the found AOB address from decimal to hexadecimal AUTOMATICALLY. I mean the rest of the stuff I worked with in classMemory outputted addresses in hex, so why does this one in particular have to be dec?
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() so that my memory tool will pick up the right memory addresses as soon as the targeted program is detected. This way I'm not forcing the user to run the target program BEFORE the memory tool, thus making it easier to use.
Here's an example. If I first launch Notepad, type Thisisatest and launch the script, the AOB is correctly found:
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
}
}
But if I first launch the script, then Notepad and type Thisisatest, the AOB will never be found even though the search is looped. Is there any workaround for this? I want to make my scripts as easy as possible for users.
Also, just a suggestion, I think classMemory should convert the found AOB address from decimal to hexadecimal AUTOMATICALLY. I mean the rest of the stuff I worked with in classMemory outputted addresses in hex, so why does this one in particular have to be dec?
YOU'RE NOT ALEXANDER
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
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()
The target process must exist when you call "new _ClassMemory()".
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.
This is a simple approach. Just wait for the target to exist before doing any memory stuff. Exit the script when the target closes.
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
When checking if the target exists or not you want to use a relatively low interval, otherwise it's possible for the target process to restart with the script missing this. You could check the PID for extra-protection, or use other means - but this is simple and works well for most purposes.
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
-
- Posts: 38
- Joined: 25 Apr 2016, 18:00
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
This is Great work!!! Thank You very much for so awesome, simple and beautiful solution!!!
Дай Вам Бог здоровья и долгих и интересных дней жизни! =)
Дай Вам Бог здоровья и долгих и интересных дней жизни! =)
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
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.
PX_distance := TargetProcess.read(0x00FB79A0 + TargetProcess.BaseAddress, "UFloat")
but I understand I made no sense since the dec version of 0x00FB79A0 (16480672) would work too there.
Anyway, thanks for the help on the AOB scans and for isHandleValid, they worked great, this is the looped code I've been using on my own tool:
Spoiler
Moving on to another matter, I can't get suspend() and resume() to work through classMemory. This is the code I've been trying:
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
I would also like to recommend you to somehow implement multiple methods of process suspension, as they have different pros and cons.
METHOD 1:
NtSuspendProcess/ZwSuspendProcess and NtResumeProcess/ZwResumeProcess. This is the one your classMemory uses but I can't get it to work.
Working example with a function that checks weather a process has been suspended or not before attempting to suspend:
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
- if you send the suspend command to a process like 3 times in a row, you also need to resume it the same amount of times (or more) to actually resume it, so there needs to be a way to properly check the current suspension status of a process beforehand (provided in the above example)
METHOD 2:
DebugActiveProcess and DebugActiveProcessStop
example:
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
- can suspend multiple times without the need to resume the same amount of times unlike METHOD 1
METHOD 3:
SuspendThread/Wow64SuspendThread and ResumeThread/Wow64ResumeThread. Suspending every thread of a process should result in the same outcome as the previous methods.
I don't have a ready to use code for this, I did it with Process Hacker, but these resources may be useful
https://autohotkey.com/board/topic/2124 ... endthread/
https://autohotkey.com/boards/viewtopic.php?t=19323
https://autohotkey.com/boards/viewtopic.php?t=24055
PROS:
- this method can work where METHOD 1 and 2 fail with some protected processes, bypassing "Access denied" errors
Black Desert Online for example is protected by anticheat XIGNCODE, and this has been the only suspension method that worked.
CONS:
looks complicated for multi-threaded processes
Last edited by WAZAAAAA on 11 Oct 2017, 02:03, edited 3 times in total.
YOU'RE NOT ALEXANDER
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
For suspend and resume see here:
https://autohotkey.com/boards/viewtopic ... 012#p96012
(A 64-bit application can suspend a WOW64 thread using the Wow64SuspendThread function.)
https://autohotkey.com/boards/viewtopic ... 012#p96012
(A 64-bit application can suspend a WOW64 thread using the Wow64SuspendThread function.)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Re: Memory Process reading/Writing & Pattern Scans (Array of bytes)
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")
It still refuses to show text beyond the first encountered null.
This is my code for the AOB scan to find the bytes for Hotbar.dat
Code: Select all
Hotbar:= XIV.hexStringtoPattern("48 4F 54 42 41 52 2E 44 41 54 00 00 01 00 00 00 70")
which will cover all the skills/keybinds on the hotbars.
Reading all these hotkeys, quite simply informs the program I'm writing, what all the keybinds are and what to press for said abilities.
In this example here, I simply want to read all the Abilities on my Final Fantasy 14 hotbar?
Lol, I don't understand why that Length size is there is null completely negates it.
AHK will absolutely display the string HOTBAR.DAT with the readString(address,, encoding :="UTF-8") function. Japanese if its UTF-16.
However, it will not read beyond the 0 byte after that.
So what's the point of specifying length?
Is it possible to read all the hotkey text, even across the encountered nulls between the skills?
AOB scan isn't optimal, because it won't read changes to the hotbar in real-time.
People change hotkeys and move skills around all the time. The pattern won't be the same, and the bot will press wrong buttons.
What other function ignores nulls, reads between a range of addresses, and obeys specified byte length?
AOB scans take too damn long, because I have to write AOB patters for every single spell/skill keybind in Final Fantasy.
@rommmcek
Currently, I am using an increment of =+1 from the base address to read all the bytes individually and parse each character/letter in order, and paste all that text file, and read from file.
But that's so exhausting. If only it can read it all.
A readstring function that ignores the nulls and obeys the length would be simple, if it exists.
@RHCP
Any solution you can give just to read text across the range of 2 specified addresses would be invaluable. Please HALP! Thanks
Return to “Scripts and Functions (v1)”
Who is online
Users browsing this forum: No registered users and 106 guests