[StructLayout(LayoutKind.Sequential)] struct WowObjStruct { IntPtr vtable; // 0x00 public IntPtr Descriptors; // 0x4 IntPtr unk1; // 0x8 public int ObjectType; // 0xC int unk3; // 0x10 IntPtr unk4; // 0x14 IntPtr unk5; // 0x18 IntPtr unk6; // 0x1C IntPtr unk7; // 0x20 IntPtr unk8; // 0x24 public ulong Guid; // 0x28 }
public enum WoWObjectType : int { Object = 0, Item = 1, Container = 2, Unit = 3, Player = 4, GameObject = 5, DynamicObject = 6, Corpse = 7, AreaTrigger = 8, SceneObject = 9, NumClientObjectTypes = 0xA, None = 0x270f, } [Flags] public enum WoWObjectTypeFlags { Object = 1 << WoWObjectType.Object, Item = 1 << WoWObjectType.Item, Container = 1 << WoWObjectType.Container, Unit = 1 << WoWObjectType.Unit, Player = 1 << WoWObjectType.Player, GameObject = 1 << WoWObjectType.GameObject, DynamicObject = 1 << WoWObjectType.DynamicObject, Corpse = 1 << WoWObjectType.Corpse, AreaTrigger = 1 << WoWObjectType.AreaTrigger, SceneObject = 1 << WoWObjectType.SceneObject }
public class WowObject { private IntPtr BaseAddress; private WowObjStruct ObjectData; public WowObject(IntPtr address) { BaseAddress = address; ObjectData = Memory.Process.Read<WowObjStruct>(BaseAddress); } public bool IsValid { get { return BaseAddress != IntPtr.Zero; } } public T GetValue<T>(Enum index) where T : struct { return Memory.Process.Read<T>(ObjectData.Descriptors + (int)index * IntPtr.Size); } public void SetValue<T>(Enum index, T val) where T : struct { Memory.Process.Write<T>(ObjectData.Descriptors + (int)index * IntPtr.Size, val); } public bool IsA(WoWObjectTypeFlags flags) { return (GetValue<int>(Descriptors.ObjectFields.Type) & (int)flags) != 0; } }
public enum ObjectFields { Guid = 0, Data = 2, Type = 4, EntryId = 5, DynamicFlags = 6, Scale = 7, End = 8, } [Flags] public enum ObjectDynamicFlags : uint { Invisible = 1 << 0, Lootable = 1 << 1, TrackUnit = 1 << 2, TaggedByOther = 1 << 3, TaggedByMe = 1 << 4, Unknown = 1 << 5, Dead = 1 << 6, ReferAFriendLinked = 1 << 7, IsTappedByAllThreatList = 1 << 8, }
public class WowObject { // public int Entry { get { return GetValue<int>(ObjectFields.EntryId); } } }
[StructLayout(LayoutKind.Sequential)] struct TSExplicitList // 12 { public TSList baseClass; // 12 } [StructLayout(LayoutKind.Sequential)] struct TSList // 12 { public int m_linkoffset; // 4 public TSLink m_terminator; // 8 } [StructLayout(LayoutKind.Sequential)] struct TSLink // 8 { public IntPtr m_prevlink; //TSLink *m_prevlink // 4 public IntPtr m_next; // C_OBJECTHASH *m_next // 4 } [StructLayout(LayoutKind.Sequential)] struct TSHashTable // 44 { public IntPtr vtable; // 4 public TSExplicitList m_fulllist; // 12 public int m_fullnessIndicator; // 4 public TSGrowableArray m_slotlistarray; // 20 public int m_slotmask; // 4 } [StructLayout(LayoutKind.Sequential)] struct TSBaseArray // 16 { public IntPtr vtable; // 4 public uint m_alloc; // 4 public uint m_count; // 4 public IntPtr m_data;//TSExplicitList* m_data; // 4 } [StructLayout(LayoutKind.Sequential)] struct TSFixedArray // 16 { public TSBaseArray baseClass; // 16 } [StructLayout(LayoutKind.Sequential)] struct TSGrowableArray // 20 { public TSFixedArray baseclass; // 16 public uint m_chunk; // 4 } [StructLayout(LayoutKind.Sequential)] struct CurMgr // 248 bytes x86, 456 bytes x64 { public TSHashTable VisibleObjects; // m_objects 44 public TSHashTable LazyCleanupObjects; // m_lazyCleanupObjects 44 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] // m_lazyCleanupFifo, m_freeObjects, m_visibleObjects, m_reenabledObjects, whateverObjects... public TSExplicitList[] Links; // Links[10] has all objects stored in VisibleObjects it seems 12 * 11 = 132 #if !X64 public int Unknown1; // wtf is that and why x86 only? // 4 public int Unknown2; // not sure if this actually reflects the new object manager structure, but it does get the rest of the struct aligned correctly public int Unknown3; // not sure if this actually reflects the new object manager structure, but it does get the rest of the struct aligned correctly #endif public ulong ActivePlayer; // 8 public int PlayerType; // 4 public int MapId; // 4 public IntPtr ClientConnection; // 4 public IntPtr MovementGlobals; // 4 }
public enum ObjectManager { connection = 0xEC4140, objectManager = 0x462c, }
push 0 push 0A9Fh push offset aObjectmgrclien ; "ObjectMgrClient.cpp" push 100h call sub_5DC588 test eax, eax jz short loc_79EEEB mov ecx, eax call sub_79E1E1 jmp short loc_79EEED loc_79EEEB: xor eax, eax loc_79EEED: mov ecx, dword_12C4140 fldz mov [ecx+462Ch], eax
public class ObjectManager : IEnumerable<WowObject> { private CurMgr _curMgr; private IntPtr _baseAddress; private WowGuid _activePlayer; private WowPlayer _activePlayerObj; public void UpdateBaseAddress() { var connection = Memory.Process.Read<IntPtr>((int)ObjectManager.connection, true); _baseAddress = Memory.Process.Read<IntPtr>(connection + (int)ObjectManager.objectManager); } private IntPtr BaseAddress { get { return _baseAddress; } } public WowGuid ActivePlayer { get { return _activePlayer; } } public WowPlayer ActivePlayerObj { get { return _activePlayerObj; } } public IntPtr ClientConnection { get { return _curMgr.ClientConnection; } } public IntPtr FirstObject() { return _curMgr.VisibleObjects.m_fulllist.baseClass.m_terminator.m_next; } public IntPtr NextObject(IntPtr current) { return Memory.Process.Read<IntPtr>(current + _curMgr.VisibleObjects.m_fulllist.baseClass.m_linkoffset + IntPtr.Size); } public IEnumerable<WowObject> GetObjects() { _curMgr = Memory.Process.Read<CurMgr>(BaseAddress); _activePlayer = new WowGuid(_curMgr.ActivePlayer); IntPtr first = FirstObject(); while (((first.ToInt64() & 1) == 0) && first != IntPtr.Zero) { var wowObject = new WowObject(first); if (wowObject.Guid.Value == _curMgr.ActivePlayer) { _activePlayerObj = new WowPlayer(first); } first = NextObject(first); } } public IEnumerator<WowObject> GetEnumerator() { return GetObjects().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
public enum UnitField { CachedSubName = 0, UnitClassificationOffset2 = 32, CachedQuestItem1 = 48, CachedTypeFlag = 76, IsBossOffset2 = 76, CachedModelId1 = 92, CachedName = 108, UNIT_SPEED = 128, TaxiStatus = 0xc0, TransportGUID = 2096, UNIT_FIELD_X = 0x838, UNIT_FIELD_Y = 0x83C, UNIT_FIELD_Z = 0x840, UNIT_FIELD_R = 2120, DBCacheRow = 2484, IsBossOffset1 = 2484, UnitClassificationOffset1 = 2484, CanInterrupt = 3172, CastingSpellID = 3256, ChannelSpellID = 3280, } public enum UnitFields { Charm = ObjectFields.End + 0, Summon = 10, Critter = 12, CharmedBy = 14, SummonedBy = 16, CreatedBy = 18, DemonCreator = 20, Target = 22, BattlePetCompanionGUID = 24, ChannelObject = 26, ChannelSpell = 28, SummonedByHomeRealm = 29, Sex = 30, DisplayPower = 31, OverrideDisplayPowerID = 32, Health = 33, Power = 34, MaxHealth = 39, MaxPower = 40, PowerRegenFlatModifier = 45, PowerRegenInterruptedFlatModifier = 50, Level = 55, EffectiveLevel = 56, FactionTemplate = 57, VirtualItemID = 58, Flags = 61, Flags2 = 62, AuraState = 63, AttackRoundBaseTime = 64, RangedAttackRoundBaseTime = 66, BoundingRadius = 67, CombatReach = 68, DisplayID = 69, NativeDisplayID = 70, MountDisplayID = 71, MinDamage = 72, MaxDamage = 73, MinOffHandDamage = 74, MaxOffHandDamage = 75, AnimTier = 76, PetNumber = 77, PetNameTimestamp = 78, PetExperience = 79, PetNextLevelExperience = 80, ModCastingSpeed = 81, ModSpellHaste = 82, ModHaste = 83, ModRangedHaste = 84, ModHasteRegen = 85, CreatedBySpell = 86, NpcFlag = 87, EmoteState = 89, Stats = 90, StatPosBuff = 95, StatNegBuff = 100, Resistances = 105, ResistanceBuffModsPositive = 112, ResistanceBuffModsNegative = 119, BaseMana = 126, BaseHealth = 127, ShapeshiftForm = 128, AttackPower = 129, AttackPowerModPos = 130, AttackPowerModNeg = 131, AttackPowerMultiplier = 132, RangedAttackPower = 133, RangedAttackPowerModPos = 134, RangedAttackPowerModNeg = 135, RangedAttackPowerMultiplier = 136, MinRangedDamage = 137, MaxRangedDamage = 138, PowerCostModifier = 139, PowerCostMultiplier = 146, MaxHealthModifier = 153, HoverHeight = 154, MinItemLevel = 155, MaxItemLevel = 156, WildBattlePetLevel = 157, BattlePetCompanionNameTimestamp = 158, InteractSpellID = 159, End = 160, } public class WowUnit : WowObject { public WowUnit(IntPtr address) : base(address) { } public int Health { get { return GetValue<int>(UnitFields.Health); } } public int MaxHealth { get { return GetValue<int>(UnitFields.MaxHealth); } } public bool IsAlive { get { return !IsDead; } } public bool IsDead { get { return this.Health <= 0 || (DynamicFlags & ObjectDynamicFlags.Dead) != 0; } } public ulong TransportGuid { get { return GetValue<ulong>(UnitField.TransportGUID); } } public bool InTransport { get { return TransportGuid > 0; } } public Vector3 Position { get { if (Pointer == IntPtr.Zero) return Vector3.Zero; if (InTransport) { var wowObject = Memory.ObjectManager.GetObjectByGUID(TransportGuid); if (wowObject != null) { var wowUnit = new WowUnit(wowObject.Pointer); if (wowUnit.IsValid && wowUnit.IsAlive) return wowUnit.Position; } } var position = new Vector3( Memory.Process.Read<float>(Pointer + (int)UnitField.UNIT_FIELD_X), Memory.Process.Read<float>(Pointer + (int)UnitField.UNIT_FIELD_Y), Memory.Process.Read<float>(Pointer + (int)UnitField.UNIT_FIELD_Z), "None"); return position; } } }
Source: https://habr.com/ru/post/251353/
All Articles