Hi% username%! So, let's continue writing our bot. From past articles, we learned how to find the address of the intercepted function for DirectX 9 and 11, as well as execute arbitrary assembly code in the main stream of the game and hide from various methods of protection. Now all this knowledge can be applied in real combat conditions. And we begin with the study of the program for which we write the bot. [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