Code:
SetBatchLines, -1
DetectHiddenWindows, On
cliname=Path of Exile
cliexe=Client.exe
trayNotifications:=true ;display tray notifications about script actions : drinking potions, autoquitting
autoPotionsWatchdogPeriod:=100 ;milliseconds, decrease this value to have script recheck life/mana/flasks availability more often/increase chances of getting saved from death in time
lagCompensation:=50
autoQuitMode:=1 ; default autoQuit method : 0 =winKill, 1 = exit to login screen
autoQuitPauseBeforeClick:=100
autoQuitSoftToleranceBeforeKill:=2000 ; try to quit to loginscreen at most milliseconds before killing game window(in case we can't quit by clicking menu option for some reason)
PlayerConfig:={}
PlayerConfig["Default"]:={minLifeRatioToDrink: 0.45, minManaRatioToDrink: 0.55, minManaToDrink: 40, minLifeRatioToPopGranite: 0.65, minLifeRatioToQuit: 0.35} ; disableAutoPotions:true, minLifeRatioToQuit:, minNShieldRatioToQuit: , HasZealotsOath: false, }
PlayerConfig["Default"].FlaskConfig:=[]
PlayerConfig["Default"].FlaskConfig[1]:={Hotkey:"{1 Down 1 UP}"} ; ,OverrideFlaskDuration:70, instantRecoveryOnLowLife:true, } ;specify override recovery time in deciseconds, e.g. 7 seconds = 70
PlayerConfig["Default"].FlaskConfig[2]:={Hotkey:"{2 Down 2 UP}"}
PlayerConfig["Default"].FlaskConfig[3]:={Hotkey:"{3 Down 3 UP}"}
PlayerConfig["Default"].FlaskConfig[4]:={Hotkey:"{4 Down 4 UP}"}
PlayerConfig["Default"].FlaskConfig[5]:={Hotkey:"{5 Down 5 UP}"}
PlayerConfig["YourHardcorePlayerName"]:={minLifeRatioToDrink: 0.7, minManaRatioToDrink: 0.35, minManaToDrink: 70, minLifeRatioToQuit: 0.4}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig:=[]
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[1]:={Hotkey:"{1 Down 1 UP}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[2]:={Hotkey:"{2 Down 2 UP}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[3]:={Hotkey:"{3 Down 3 UP}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[4]:={Hotkey:"{4 Down 4 UP}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[5]:={Hotkey:"{5 Down 5 UP}"}
autoPotionsState:=true
WindowQueuedFlaskEffects:=[] ;keyed by "%hwnd%%CurrPid%", hpQueueEndtime, manaQueueEndtime
baseMgrPtr:=0x71E008 ; 0.11.3b delete for script to try to auto-scan for it in newer versions, scan takes 4-5 seconds
basePtrAoBArray:=[0x6A,0xFF,0x68,"?","?","?","?",0x50,0x64,"?","?","?","?","?","?",0xA1,"?","?","?","?",0x81,0xEC,"?","?","?","?",0x53,0x55,0x56,0x57,0x33,0xFF,0x3B,0xC7]
basePtrAobOffset:=0x10
WindowBasicsCache:=[] ; keyed by "%hwnd%%CurrPid%", entries are objects with properties processHandle, moduleBase, moduleSize, baseFramePtr
#Include AutoHotkeyMemoryLib.ahk
Loop
{
AutoPotions()
}
GetWindowBasics(hwnd, byref mB="", byref pH="", byref mS="")
{
global WindowBasicsCache
global cliexe
WinGet, CurrPid, PID, ahk_id %hwnd%
k="%hwnd%%CurrPid%"
mB:=WindowBasicsCache[k].mBase
mS:=WindowBasicsCache[k].mSize
if mB=
{
WindowBasicsCache[k]:=Object()
GetModuleInfo(cliexe, CurrPid, mB, mS)
if (mB="" || mS="")
{
MsgBox, Failed to obtain moduleBase or moduleSize for PID %CurrPid%, script will now terminate
ExitApp
}
WindowBasicsCache[k].mBase:=mB
WindowBasicsCache[k].mSize:=mS
}
pH:=WindowBasicsCache[k].ProcessHandle
if pH=
{
pH:=GetProcessHandle(CurrPid)
if (pH="" || pH=-1)
{
MsgBox, Invalid process handle obtained for PID %CurrPid%, script will now terminate
ExitApp
}
WindowBasicsCache[k].ProcessHandle:=pH
}
}
ScanBaseMgrPtr(mBase,pH,moduleSize)
{
global basePtrAoBArray
global basePtrAobOffset
global baseMgrPtr
aobResult:=AobScan(pH,mBase,moduleSize,basePtrAoBArray)
if aobResult
{
SetFormat, IntegerFast, hex
baseMgrPtr:=ReadMemUInt(pH,mBase+aobResult+basePtrAobOffset)-mBase
MsgBox, PoE Base ptr found with AoB Scan baseMgrPtr = %baseMgrPtr%, save this value to script for quick startup
SetFormat, IntegerFast, dec
}
else
{
MsgBox, baseMgrPtr not found with AoBScan, script will now terminate
ExitApp
}
}
GetFrameBase(hwnd)
{
global baseMgrPtr
global WindowBasicsCache
WinGet, CurrPid, PID, ahk_id %hwnd%
k="%hwnd%%CurrPid%"
fB:=WindowBasicsCache[k].fBase
if fB=
{
GetWindowBasics(hwnd, mBase, pH, mSize)
if baseMgrPtr=
{
ScanBaseMgrPtr(mBase, pH, mSize)
}
fB:=GetMultilevelPointer(pH,[mBase+baseMgrPtr,4,0x7C,0x94])
WindowBasicsCache[k].fBase:=fB
}
return fB
}
GetUiBase(hwnd)
{
global baseMgrPtr
GetWindowBasics(hwnd, mBase, pH, mSize)
if baseMgrPtr=
{
ScanBaseMgrPtr(mBase, pH, mSize)
}
FrameBase:=GetFrameBase(hwnd)
if (FrameBase="" || FrameBase=0)
return
uiBase:=GetMultilevelPointer(pH,[FrameBase+0x38,0x78,0xA4,0x50])
return uiBase
}
ReadClientResolution(hwnd, ByRef w, ByRef h)
{
GetWindowBasics(hwnd,mBase,pH)
if (mBase!=0 && pH && pH!=-1)
{
FrameBase:=GetFrameBase(hwnd)
w:=ReadMemUInt(pH,FrameBase+0x1340)
h:=ReadMemUInt(pH,FrameBase+0x1344)
return true
}
}
ReadPlayerStats(hwnd, byRef PlayerStats)
{
GetWindowBasics(hwnd, mBase, pH)
fBase:=GetFrameBase(hwnd)
PlayerBase:=GetMultilevelPointer(pH,[fBase+0xb4,0x59C])
PlayerMain:=ReadMemUInt(pH,PlayerBase+4)
PlayerStatsOffset:=ReadMemUInt(pH,PlayerMain+0xC)
PlayerStats.MaxHP:=ReadMemUInt(pH,PlayerStatsOffset+0x50)
PlayerStats.CurrHP:=ReadMemUInt(pH,PlayerStatsOffset+0x54)
PlayerStats.ReservedHPFlat:=ReadMemUInt(pH,PlayerStatsOffset+0x5C)
PlayerStats.ReservedHPPercent:=ReadMemUInt(pH,PlayerStatsOffset+0x60)
PlayerStats.MaxMana:=ReadMemUInt(pH,PlayerStatsOffset+0x74)
PlayerStats.ReservedManaFlat:=ReadMemUInt(pH,PlayerStatsOffset+0x80)
PlayerStats.ReservedManaPercent:=ReadMemUInt(pH,PlayerStatsOffset+0x84)
PlayerStats.CurrMana:=ReadMemUInt(pH,PlayerStatsOffset+0x78)
PlayerStats.MaxNShield:=ReadMemUInt(pH,PlayerStatsOffset+0x98)
PlayerStats.CurrNShield:=ReadMemUInt(pH,PlayerStatsOffset+0x9C)
if (ReadMemUInt(pH, ReadMemUInt(pH,PlayerMain+0x14)+0x24)<8) ;names shorter than 7 chars are stored immediately in component
PlayerStats.Name:=ReadMemStr(pH, ReadMemUint(pH,PlayerMain+0x14)+0x10,100,"UTF-16") ;immediate name in component
else
PlayerStats.Name:=ReadMemStr(pH, GetMultilevelPointer(pH,[PlayerMain+0x14,0x10]),100,"UTF-16") ; otherwise pointer to name is stored
}
ReadFlasksData(hwnd, byRef FlasksData)
{
GetWindowBasics(hwnd, mBase, pH)
UiBase:=GetUiBase(hwnd)
if (!UiBase) ;not InGame
return
FlaskInvBase:=GetMultilevelPointer(pH,[UiBase+0x8d0,0x8e8,0x28])
Loop, 5
{
currFlaskPtr:=ReadMemUInt(pH,FlaskInvBase+(A_Index-1)*4)
if (currFlaskPtr!=0) ; there's a flask in said slot
{
FlasksData[A_Index]:={}
FlaskChargesPtr:=GetMultilevelPointer(ph,[currFlaskPtr,4,0x1C,4,4,0xC])
FlasksData[A_Index].ChargesCurrent:=ReadMemUInt(pH,FlaskChargesPtr+0xC)
FlasksData[A_Index].ChargesPerUse:=ReadMemUInt(pH,ReadMemUInt(pH,FlaskChargesPtr+8)+0xC)
if (FlasksData[A_Index].ChargesCurrent < FlasksData[A_Index].ChargesPerUse) ; not enough charges in this flask to use it, don't bother
continue
FlaskMetadataPtr:=GetMultilevelPointer(ph,[currFlaskPtr,0,8])
FlaskMetadataStr:=ReadMemStr(ph,FlaskMetadataPtr,70,"UTF-16")
FlaskTypeStr:=SubStr(FlaskMetadataStr,23)
FlasksData[A_Index].type:=FlaskTypeStr
FlaskLocalstatsPtr:=GetMultilevelPointer(ph,[currFlaskPtr,4,0x18,0x20,0xC])
if InStr(FlaskTypeStr, "Life")
{
FlasksData[A_Index].HPRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+4)
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0xC)
}
if InStr(FlaskTypeStr, "Mana")
{
FlasksData[A_Index].ManaRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+4)
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0xC)
}
if InStr(FlaskTypeStr, "Hybrid")
{
FlasksData[A_Index].HPRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+4)
FlasksData[A_Index].ManaRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+0xC)
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0x14)
}
if InStr(FlaskTypeStr, "FlaskUtility")
{
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0x4)
}
}
}
}
IsInGame(hwnd)
{
if (hwnd=0 || hwnd="")
return false
GetWindowBasics(hwnd,mBase,pH)
if (mBase="" || mBase=0 || pH="" || pH=-1)
return false
fBase:=GetFrameBase(hwnd)
if (fBase="" || fBase=0)
return false
localConnection:=ReadMemUInt(pH,fBase+0xb8)
if (localConnection=0 || localConnection="")
return false
else
return true
}
SetGameStateMenu(hwnd)
{
if (hwnd=0 || hwnd="")
return false
GetWindowBasics(hwnd,mBase,pH)
if (mBase="" || mBase=0 || pH="" || pH=-1)
return false
fBase:=GetFrameBase(hwnd)
if (fBase="" || fBase=0)
return false
localConnection:=ReadMemUInt(pH,fBase+0xb8)
if (localConnection!="" && localConnection!=0)
{
WriteMemUInt(pH,localConnection+0x1ff8,1)
}
}
ReadCursorScreenPosition(hwnd,ByRef cX, ByRef cY)
{
GetWindowBasics(hwnd,mBase,pH)
if (mBase!=0 && pH && pH!=-1)
{
FrameBase:=GetFrameBase(hwnd)
cX:=ReadMemSInt(pH,FrameBase+0x155c)
cY:=ReadMemSInt(pH,FrameBase+0x1560)
return true
}
}
ScreenToClient(hwnd, ByRef x, ByRef y)
{
VarSetCapacity(pt, 8)
NumPut(x, pt, 0)
NumPut(y, pt, 4)
DllCall("ScreenToClient", "uint", hwnd, "uint", &pt)
x := NumGet(pt, 0, "int")
y := NumGet(pt, 4, "int")
VarSetCapacity(pt, 0)
}
GetClientCoords(byRef mx, byRef my)
{
hwnd:=WinActive("A")
CoordMode, Mouse, Screen
MouseGetPos, mx, my
ScreenToClient(hwnd,mx,my) ;get mouse pos relative to window client rect
}
GetFractionalCoords(ByRef fX, ByRef fY)
{
hwnd:=WinActive("A")
if (!IsInGame(hwnd))
GetClientCoords(mx,my)
else
ReadCursorScreenPosition(hwnd,mx,my)
ReadClientResolution(hwnd,w,h)
fX:=mx/w
fY:=my/h
}
GetClientCoordsFromFractional(hwnd, fX,fY, ByRef cX, ByRef cY)
{
ReadClientResolution(hwnd,w,h)
cX:=fX*w
cY:=fY*h
}
QuitToLoginScreen(hwnd)
{
if (!IsInGame(hwnd))
{
return
}
SetGameStateMenu(hwnd)
}
GetMaxChargesFlaskOfType(ByRef FlasksData,TypeStr)
{
currMaxCharges:=0
Loop, 5
if (InStr(FlasksData[A_Index].type,TypeStr))
{
if FlasksData[A_Index].ChargesCurrent>currMaxCharges
{
currMaxI:=A_Index
currMaxCharges:=FlasksData[A_Index].ChargesCurrent
}
}
return currMaxI
}
AutoPotions()
{
global autoPotionsWatchdogPeriod
global lagCompensation
global PlayerConfig
global WindowQueuedFlaskEffects
global cliname
global cliexe
global trayNotifications
global autoQuitMode
if (autoPotionsState!=true)
return
WinGet, WinID, List, %cliname%
Loop, %WinID%
{
WinGet, ProcModuleName, ProcessName, % "ahk_id" WinID%A_Index%
If(ProcModuleName!=cliexe) ; got a window with title "Path of Exile" but exe is not Client.exe, perhaps we have browser window open with PoE site, ignore it
continue
if (!IsInGame(WinID%A_Index%)) ;not ingame
continue
if (WinID%A_Index%=WinActive("A"))
ThisID:=WinActive("A")
PlayerStats:={}
ReadPlayerStats(WinID%A_Index%, PlayerStats)
if (PlayerStats.MaxHP<1 || PlayerStats.CurrHP=0) ;dead, don't bother
continue
if (PlayerConfig.HasKey(PlayerStats.Name))
CurrentConfig:=PlayerConfig[PlayerStats.Name]
else
CurrentConfig:=PlayerConfig["Default"]
if PlayerStats.MaxNShield>0
{
currNShieldRatio:=PlayerStats.CurrNShield/PlayerStats.MaxNShield
}
if (PlayerStats.MaxHP>1)
{
currLifeRatio:=PlayerStats.CurrHP/(PlayerStats.MaxHP-PlayerStats.ReservedHPFlat-PlayerStats.MaxHP*PlayerStats.ReservedHPPercent/100)
}
if CurrentConfig.HasZealotsOath
{
currLifeRatio:=currNShieldRatio
}
if (PlayerStats.MaxMana>0)
{
currManaRatio:=PlayerStats.CurrMana/(PlayerStats.MaxMana-PlayerStats.ReservedManaFlat-PlayerStats.MaxMana*PlayerStats.ReservedManaPercent/100)
}
if (currLifeRatio<CurrentConfig.minLifeRatioToQuit || currNShieldRatio<CurrentConfig.minNShieldRatioToQuit)
{
if (autoQuitMode=0)
{
TrayTip, PoE autoPotions AutoQuit by closing window, specified min life reached, %A_Space% , 2
WinKill, % "ahk_id" WinID%A_Index%
}
else if (autoQuitMode=1)
{
QuitToLoginScreen(WinID%A_Index%)
}
autoQuit:=1
continue
}
if (CurrentConfig.disableAutoPotions)
continue
FlasksData:=[]
ReadFlasksData(WinID%A_Index%,FlasksData)
WinGet, CurrPID, PID, % "ahk_id" WinID%A_Index%
hwnd:=WinID%A_Index%
k="%hwnd%%CurrPid%"
if (!WindowQueuedFlaskEffects.HasKey(k))
{
WindowQueuedFlaskEffects[k]:={}
}
if (currLifeRatio<CurrentConfig.minLifeRatioToPopGranite || currNShieldRatio<CurrentConfig.minNShieldRatioToPopGranite)
if ((!WindowQueuedFlaskEffects[k].HasKey("graniteQueueEndtime")) || (A_TickCount>=(WindowQueuedFlaskEffects[k].graniteQueueEndtime-lagCompensation)))
{
flaskNum:=GetMaxChargesFlaskOfType(FlasksData,"FlaskUtility5") ; granite flask
if (flaskNum!="")
{
if CurrentConfig.FlaskConfig[flaskNum].HasKey("OverrideFlaskDuration")
EffectDuration:=CurrentConfig.FlaskConfig[flaskNum].OverrideFlaskDuration
else
EffectDuration:=FlasksData[flaskNum].EffectDuration
WindowQueuedFlaskEffects[k].graniteQueueEndtime:=A_TickCount+EffectDuration*100
if (trayNotifications)
{
pname:=PlayerStats.Name
TrayTip, PoE autoPotions popping Granite flask %flaskNum% on %pname%, %A_Space% , 2
}
hKey:=CurrentConfig.FlaskConfig[flaskNum].Hotkey
ControlSend,,%hkey%, % "ahk_id" hwnd
break
}
}
if (currLifeRatio=1)
WindowQueuedFlaskEffects[k].hpQueueEndtime:=A_TickCount
if (currManaRatio=1)
WindowQueuedFlaskEffects[k].ManaQueueEndtime:=A_TickCount
if (currLifeRatio<CurrentConfig.minLifeRatioToDrink || (PlayerStats.CurrHP<CurrentConfig.minLifeToDrink))
if ((!WindowQueuedFlaskEffects[k].HasKey("hpQueueEndtime")) || (A_TickCount>=(WindowQueuedFlaskEffects[k].hpQueueEndtime-lagCompensation)))
{
tflaskNum1:=GetMaxChargesFlaskOfType(FlasksData,"FlaskLife")
tflaskNum2:=GetMaxChargesFlaskOfType(FlasksData,"FlaskHybrid")
if ((tflaskNum1!=) && (tflaskNum2!=))
flaskNum:=(FlasksData[tflaskNum1].ChargesCurrent>FlasksData[tflaskNum2].ChargesCurrent) ? tflaskNum1 : tflaskNum2
else
{
if (tflaskNum1!="")
flaskNum:=tflaskNum1
if (tflaskNum2!="")
flaskNum:=tflaskNum2
}
if (flaskNum!="")
{
if CurrentConfig.FlaskConfig[flaskNum].HasKey("OverrideFlaskDuration")
EffectDuration:=CurrentConfig.FlaskConfig[flaskNum].OverrideFlaskDuration
else
EffectDuration:=FlasksData[flaskNum].EffectDuration
if ((CurrentConfig.FlaskConfig[flaskNum].instantRecoveryOnLowLife) && ((PlayerStats.CurrHP/PlayerStats.MaxHP)<=0.35)) ; "Low life" can be caused by auras hp reservation from blood magic
EffectDuration:=lagCompensation
WindowQueuedFlaskEffects[k].hpQueueEndtime:=A_TickCount+EffectDuration*100
if (FlasksData[flaskNum].HasKey("ManaRegAmount")) ; hybrid flask
WindowQueuedFlaskEffects[k].ManaQueueEndtime:=A_TickCount+EffectDuration*100
if (trayNotifications)
{
pname:=PlayerStats.Name
TrayTip, PoE autoPotions sipping HP flask %flaskNum% on %pname%, %A_Space% , 2
}
hKey:=CurrentConfig.FlaskConfig[flaskNum].Hotkey
ControlSend,,%hkey%, % "ahk_id" hwnd
break
}
}
if (PlayerStats.MaxMana>0 && (currManaRatio<CurrentConfig.minManaRatioToDrink || PlayerStats.CurrMana<CurrentConfig.minManaToDrink))
if ((!WindowQueuedFlaskEffects[k].HasKey("ManaQueueEndtime")) || (A_TickCount>=(WindowQueuedFlaskEffects[k].ManaQueueEndtime-lagCompensation)))
{
tflaskNum1:=GetMaxChargesFlaskOfType(FlasksData,"FlaskMana")
tflaskNum2:=GetMaxChargesFlaskOfType(FlasksData,"FlaskHybrid")
if ((tflaskNum1!=) && (tflaskNum2!=))
flaskNum:=(FlasksData[tflaskNum1].ChargesCurrent>FlasksData[tflaskNum2].ChargesCurrent) ? tflaskNum1 : tflaskNum2
else
{
if (tflaskNum1!="")
flaskNum:=tflaskNum1
if (tflaskNum2!="")
flaskNum:=tflaskNum2
}
if (flaskNum!="")
{
if CurrentConfig.FlaskConfig[flaskNum].HasKey("OverrideFlaskDuration")
{
EffectDuration:=CurrentConfig.FlaskConfig[flaskNum].OverrideFlaskDuration
}
else
EffectDuration:=FlasksData[flaskNum].EffectDuration
if ((CurrentConfig.FlaskConfig[flaskNum].instantRecoveryOnLowLife) && ((PlayerStats.CurrHP/PlayerStats.MaxHP)<=0.35))
EffectDuration:=lagCompensation
WindowQueuedFlaskEffects[k].ManaQueueEndtime:=A_TickCount+EffectDuration*100
if (FlasksData[flaskNum].HasKey("HPRegAmount")) ; hybrid flask
WindowQueuedFlaskEffects[k].hpQueueEndtime:=A_TickCount+EffectDuration*100
hKey:=CurrentConfig.FlaskConfig[flaskNum].Hotkey
if (trayNotifications)
{
pname:=PlayerStats.Name
TrayTip, PoE autoPotions sipping mana flask %flaskNum% on %pname%, %A_Space% , 2
}
ControlSend,,%hkey%, % "ahk_id" hwnd
break
}
}
}
if ((autoQuit=1) && (ThisID!="") && (ThisID!=WinActive("A")))
WinActivate, % "ahk_id" ThisID
Sleep, %autoPotionsWatchdogPeriod%
}
F1::
global autoPotionsState
global trayNotifications
autoPotionsState:=not autoPotionsState
if (trayNotifications)
{
if (autoPotionsState=true)
TrayTip, PoE autoPotions is on, %A_Space% , 2
else
TrayTip, PoE autoPotions is off, %A_Space% , 2
}
return
F2::
GetClientCoords(mx,my)
GetFractionalCoords(fx,fy)
msgbox, mx=%mx% my=%my% fx=%fx% fy=%fy%
return
F4::
QuitToLoginScreen(WinActive("A"))
return
AutoHotkeyMemoryLib.ahk
Code:
if (A_PtrSize != 4)
{
MsgBox, You are not running 32-bit version of Autohotkey L, reinstall correct version. Script will now terminate.
ExitApp
}
GetModuleInfo(ModuleName, PID, byRef mBase="", byRef mSize="")
{
TH32CS_SNAPMODULE := 0x00000008
INVALID_HANDLE_VALUE = -1
VarSetCapacity(me32, 548, 0)
NumPut(548, me32)
snapMod := DllCall("CreateToolhelp32Snapshot", "Uint", TH32CS_SNAPMODULE, "Uint", PID)
If (snapMod = INVALID_HANDLE_VALUE) {
Return 0
}
If (DllCall("Module32First", "Uint", snapMod, "Uint", &me32)){
If StrGet(&me32 + 32, "cp0")=ModuleName
{
mBase:=NumGet(&me32 + 20)
mSize:=NumGet(&me32 + 24)
DllCall("CloseHandle", "UInt", snapMod)
Return
}
while(DllCall("Module32Next", "Uint", snapMod, "UInt", &me32))
{
If StrGet(&me32 + 32, "cp0")=ModuleName
{
mBase:=NumGet(&me32 + 20)
mSize:=NumGet(&me32 + 24)
DllCall("CloseHandle", "UInt", snapMod)
Return
}
}
}
DllCall("CloseHandle", "Uint", snapMod)
}
GetProcessHandle(pid)
{
return DllCall("OpenProcess", "UInt", 0x8|0x10|0x20, "UInt", 0, "UInt", pid, "UInt")
}
ReadMemFloat(ProcessHandle, MADDRESS)
{
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Float*",MVALUE,"UInt",4,"UInt*",0)!=0
{
return MVALUE
}
}
ReadMemUInt(ProcessHandle, MADDRESS)
{
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"UInt*",MVALUE,"UInt",4,"UInt*",0)!=0
{
return MVALUE
}
}
ReadMemSInt(ProcessHandle, MADDRESS)
{
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Int*",MVALUE,"UInt",4,"UInt*",0)!=0
{
return MVALUE
}
}
WriteMemUInt(ProcessHandle, MADDRESS, val)
{
DllCall("WriteProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"UInt*",val,"UInt",4,"UInt*",0)!=0
}
WriteMemSInt(ProcessHandle, MADDRESS, val)
{
DllCall("WriteProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Int*",val,"UInt",4,"UInt*",0)!=0
}
WriteMemFloat(ProcessHandle, MADDRESS, val)
{
DllCall("WriteProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Float*",val,"UInt",4,"UInt*",0)!=0
}
ReadMemStr(ProcessHandle, MADDRESS, maxlen=255, cp="cp0")
{
VarSetCapacity(MVALUE,maxlen)
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"PTR",&MVALUE,"UInt",maxlen,"UInt*",bytesread)!=0
{
Str:=StrGet(&MVALUE,cp)
VarSetCapacity(MVALUE,0)
return Str
}
VarSetCapacity(MVALUE,0)
}
GetMultilevelPointer(ProcessHandle, PARRAY)
{
if PARRAY._MaxIndex()<2
return
if (DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",PARRAY[1],"UInt*",currOffset,"UInt",4,"UInt*",0)!=0)
{
i:=2
while (i<=PARRAY._MaxIndex() && DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",currOffset+PARRAY[i],"UInt*",currOffset,"UInt",4,"UInt*",0)!=0)
{
i:=i+1
}
if (i>PARRAY._MaxIndex())
{
return currOffset
}
}
}
AobScan(ProcessHandle,mBase,mSize, ByRef patternArray)
{
if (patternArray._MaxIndex()>mSize)
{
MsgBox, aobscan fail : pattern array is larger than module size
return
}
VarSetCapacity(ClientCodeSegment,mSize)
if (DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",mBase,"PTR",&ClientCodeSegment,"UInt",mSize,"UInt*",bytesread)!=0)
{
pLen:=patternArray._MaxIndex()
if (bytesread<>mSize)
{
VarSetCapacity(ClientCodeSegment,0)
MsgBox, aobscan fail : mSize=%mSize% bytesread=%bytesread%
return
}
i:=0
while (i<=mSize-pLen-1)
{
j:=1
while (j<=pLen)
{
if (patternArray[j]="?" || NumGet(ClientCodeSegment, i+j-1, "UChar")=patternArray[j])
{
j:=j+1
}
else
{
break
}
}
if (j>pLen)
{
VarSetCapacity(ClientCodeSegment,0)
return i
}
i:=i+1
}
}
MsgBox, aobscan fail : pattern not found
VarSetCapacity(ClientCodeSegment,0)
}