diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d1f60f..c2b474d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this library will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.5.4] - 2023-05-19 + +### Fixed + +- Skill cooldown memory reading inconsistencies + ## [4.5.3] - 2023-05-18 ### Fixed diff --git a/SleepHunter/IO/Process/ProcessMemoryScanner.cs b/SleepHunter/IO/Process/ProcessMemoryScanner.cs index c1c665c..6fa142e 100644 --- a/SleepHunter/IO/Process/ProcessMemoryScanner.cs +++ b/SleepHunter/IO/Process/ProcessMemoryScanner.cs @@ -11,8 +11,9 @@ namespace SleepHunter.IO.Process { internal sealed class ProcessMemoryScanner : IDisposable { - static readonly uint MinimumVmAddress = 0x0040_0000; - static readonly uint MaximumVmAddress = 0xFFFF_FFFF; + const int DefaultPageSize = 0x1000; + const uint MinimumVmAddress = 0x0040_0000; + const uint MaximumVmAddress = 0xFFFF_FFFF; bool isDisposed; IntPtr processHandle; @@ -36,6 +37,9 @@ public ProcessMemoryScanner(IntPtr processHandle, bool leaveOpen = false) NativeMethods.GetNativeSystemInfo(out var sysInfo); pageSize = (int)sysInfo.PageSize; + if (pageSize <= 0) + pageSize = DefaultPageSize; + searchBuffer = new byte[pageSize]; } @@ -174,8 +178,7 @@ public IEnumerable FindAll(byte[] bytes, int size, long startingAddress for (int i = 0; i < numberOfPages; i++) { - int numberOfBytesRead; - var result = NativeMethods.ReadProcessMemory(processHandle, memoryInfo.BaseAddress + (i * pageSize), searchBuffer, searchBuffer.Length, out numberOfBytesRead); + var result = NativeMethods.ReadProcessMemory(processHandle, memoryInfo.BaseAddress + (i * pageSize), searchBuffer, searchBuffer.Length, out var numberOfBytesRead); if (!result || numberOfBytesRead != searchBuffer.Length) throw new Win32Exception("Unable to read memory page from process."); diff --git a/SleepHunter/Models/Skillbook.cs b/SleepHunter/Models/Skillbook.cs index 0341df8..83da0db 100644 --- a/SleepHunter/Models/Skillbook.cs +++ b/SleepHunter/Models/Skillbook.cs @@ -12,6 +12,10 @@ using SleepHunter.Media; using SleepHunter.Metadata; using SleepHunter.Settings; +using System.Net; +using SleepHunter.Win32; +using System.Runtime.InteropServices; +using System.Diagnostics; namespace SleepHunter.Models { @@ -265,7 +269,7 @@ public void Update(ProcessMemoryAccessor accessor) skills[i].RequiresDisarm = false; } - skills[i].IsOnCooldown = IsSkillOnCooldown(i, version, reader); + skills[i].IsOnCooldown = IsSkillOnCooldown(i, version, reader, accessor.ProcessHandle); } catch { } } @@ -285,12 +289,12 @@ public void ResetDefaults() } } - bool IsSkillOnCooldown(int slot, ClientVersion version, BinaryReader reader) + bool IsSkillOnCooldown(int slot, ClientVersion version, BinaryReader reader, IntPtr processHandle) { - if (version == null || !UpdateSkillbookCooldownPointer(version, reader)) + if (version == null || !UpdateSkillbookCooldownPointer(version, reader, processHandle)) return false; - if (baseCooldownPointer == IntPtr.Zero) + if (!IsReadableMemory(processHandle, baseCooldownPointer)) return false; long position = reader.BaseStream.Position; @@ -309,14 +313,13 @@ bool IsSkillOnCooldown(int slot, ClientVersion version, BinaryReader reader) var address = (long)baseCooldownPointer + (slot * cooldownVariable.Size); - reader.BaseStream.Position = address; - - if (address < 0x15000000 || address > 0x30000000) + if (!IsReadableMemory(processHandle, address)) return false; + reader.BaseStream.Position = address; address = reader.ReadUInt32(); - if (address < 0x15000000 || address > 0x30000000) + if (!IsReadableMemory(processHandle, address)) return false; if (offset.IsNegative) @@ -325,19 +328,19 @@ bool IsSkillOnCooldown(int slot, ClientVersion version, BinaryReader reader) address += offset.Offset; reader.BaseStream.Position = address; - var isOnCooldown = reader.ReadBoolean(); + var cooldownFlag = reader.ReadByte(); - return isOnCooldown; + return cooldownFlag != 0x00; } catch { - baseCooldownPointer = IntPtr.Zero; + ResetCooldownPointer(); return false; } finally { reader.BaseStream.Position = position; } } - bool UpdateSkillbookCooldownPointer(ClientVersion version, BinaryReader reader) + bool UpdateSkillbookCooldownPointer(ClientVersion version, BinaryReader reader, IntPtr processHandle) { if (version == null) return false; @@ -353,29 +356,62 @@ bool UpdateSkillbookCooldownPointer(ClientVersion version, BinaryReader reader) if (baseCooldownPointer != IntPtr.Zero) return true; - var ptrs = scanner.FindAllUInt32((uint)cooldownVariable.Address).ToList(); - var firstMatch = ptrs.FirstOrDefault(); + var ptrs = scanner.FindAllUInt32((uint)cooldownVariable.Address) + .Select(ptr => + { + if (cooldownVariable.Offset.IsNegative) + ptr = (IntPtr)((uint)ptr - (uint)cooldownVariable.Offset.Offset); + else + ptr = (IntPtr)((uint)ptr + (uint)cooldownVariable.Offset.Offset); + + return ptr; + }) + .Where(ptr => IsReadableMemory(processHandle, ptr)) + .ToList(); + + + foreach (var ptr in ptrs) + { + if (ptr == IntPtr.Zero) + continue; - if (firstMatch == IntPtr.Zero) - return false; + reader.BaseStream.Position = ptr; + var cooldownPtr = reader.ReadUInt32(); - if (cooldownVariable.Offset.IsNegative) - firstMatch = (IntPtr)((uint)firstMatch - (uint)cooldownVariable.Offset.Offset); - else - firstMatch = (IntPtr)((uint)firstMatch + (uint)cooldownVariable.Offset.Offset); + if (cooldownPtr == 0 || !IsReadableMemory(processHandle, cooldownPtr)) + continue; - var address = (long)firstMatch; - - reader.BaseStream.Position = address; - address = reader.ReadUInt32(); + baseCooldownPointer = (IntPtr)cooldownPtr; + Debug.WriteLine($"Found cooldown timers pointer = {cooldownPtr:X}"); + return true; + } - baseCooldownPointer = (IntPtr)address; - return true; + return false; } catch { baseCooldownPointer = IntPtr.Zero; return false; } finally { reader.BaseStream.Position = position; } } + static bool IsReadableMemory(IntPtr processHandle, long address) + { + if (address <= 0) + return false; + + var sizeOfMemoryInfo = Marshal.SizeOf(typeof(MemoryBasicInformation)); + var byteCount = (int)NativeMethods.VirtualQueryEx(processHandle, (IntPtr)address, out var memoryInfo, sizeOfMemoryInfo); + + if (byteCount <= 0) + return false; + + if (memoryInfo.Type != VirtualMemoryType.Private) + return false; + + if (memoryInfo.State == VirtualMemoryStatus.Free) + return false; + + return true; + } + #region IEnumerable Methods public IEnumerator GetEnumerator() { diff --git a/SleepHunter/SleepHunter.csproj b/SleepHunter/SleepHunter.csproj index dd1e64c..6502603 100644 --- a/SleepHunter/SleepHunter.csproj +++ b/SleepHunter/SleepHunter.csproj @@ -19,7 +19,7 @@ git https://github.com/ewrogers/SleepHunter4 https://github.com/ewrogers/SleepHunter4 - 4.5.3.0 + 4.5.4.0 2023 Erik 'SiLo' Rogers Dark Ages Automation Tool SleepHunter