WoW Memory EditingWoW Memory Editing for learning purposes only.
This section is more advanced than others on MMOwnedRead the section specific rules, infractions will be given out if u break them!That is including the expectations! - If you don't meet them then don't post
This post is in reference to [Only registered and activated users can see links. ]
I converted this to C#, and it works just as well as Cypher's original. However, I want to be able to search the AuctionList for an auction entry based on name. After spending waaaay too long trying to figure out how to pull a name from each auction entry, I've decided to see if anyone else knows how I might be able to. (I'm stuck in a rut)
I'm guessing that maybe the ItemEntry value can be used to look up the item's name in some other structure.
Can anyone who knows more about WoW's item structure than I do give me some tips?
Donate to remove ads, get your "DONATOR title, and get access to the MMOwned community's elite Shoutbawx.
Ok, Just took a look for pointers to 'Brain Hacker'. I then looked up the ItemEntry for the corresponding auction.
(These are not static addresses)
At 0CD0A360, there is a pointer to 'Brain Hacker'
At 0CD0A370, there is an integer holding the auction's ItemEntry.
I'll post any more relevant findings here..
[edit] Just checked this out for another entry, and the same holds true (add 0x10 to a certain pointer to the entry's name and bam! matching ItemEntry).
The entry equals itemid. you could use that to lookup the name not sure if ts possible with the DBC otherwise you could write à small wrapper that looks it up on wowhead
__________________
omg ive started a blog nao! what's wrong with MEH!? -> [Only registered and activated users can see links. ]
Also if you're OOP you can just read the appropriate DBC file. That's what I was doing with my packet reader. Probably the in-proc DBC is faster tho.
Pretty sure stuff like item names are not stored in the dbc's. The client queries the server on items it's never seen before, server responds with the large struct apoc mentioned (See below), which is then cached in WDB's for future reference (by version).
Here's the structure according to wcell:
Code:
public class ItemTemplate
{
public string Name { get; set; }
public uint Id { get; set; }
public ItemId ItemId { get; set; }
public ItemClass Class { get; set; }
public ItemSubClass SubClass { get; set; }
public uint DisplayId { get; set; }
public ItemQuality Quality { get; set; }
public ItemFlags Flags { get; set; }
public uint BuyPrice { get; set; }
public uint SellPrice { get; set; }
public InventorySlotType InventorySlotType { get; set; }
public ClassMask RequiredClassMask { get; set; }
public RaceMask RequiredRaceMask { get; set; }
public uint Level { get; set; }
public uint RequiredLevel { get; set; }
public SkillId RequiredSkillId { get; set; }
public uint RequiredSkillValue { get; set; }
public SpellId RequiredProfessionId { get; set; }
public uint RequiredPvPRank { get; set; }
public uint UnknownRank { get; set; }
public FactionId RequiredFactionId { get; set; }
public StandingLevel RequiredFactionStanding { get; set; }
public uint UniqueCount { get; set; }
/// <summary>
/// The size of a stack of this item.
/// </summary>
public uint MaxAmount { get; set; }
public int ContainerSlots { get; set; }
public StatModifier[] Mods { get; set; }
public DamageInfo[] Damages { get; set; }
public int[] Resistances { get; set; }
public int AttackTime { get; set; }
public ItemProjectileType ProjectileType { get; set; }
public float RangeModifier { get; set; }
public ItemBondType BondType { get; set; }
public string Description { get; set; }
public uint PageTextId { get; set; }
public uint PageCount { get; set; }
public PageMaterial PageMaterial { get; set; }
/// <summary>
/// The Id of the Quest that will be started
/// when this Item is used.
/// </summary>
public uint QuestId { get; set; }
public uint LockId { get; set; }
public Material Material { get; set; }
public SheathType SheathType { get; set; }
public uint RandomPropertiesId { get; set; }
public uint RandomSuffixId { get; set; }
public uint BlockValue { get; set; }
public ItemSetId SetId { get; set; }
public int MaxDurability { get; set; }
public ZoneId ZoneId { get; set; }
public MapId MapId { get; set; }
public ItemBagFamilyMask BagFamily { get; set; }
public TotemCategory TotemCategory { get; set; }
public GemSocketInfo[] Sockets { get; set; }
/// <summary>
///
/// </summary>
public uint SocketBonusEnchantId { get; set; }
public ItemEnchantmentEntry SocketBonusEnchant { get; set; }
public uint GemPropertiesId { get; set; }
public GemProperties GemProperties { get; set; }
public int RequiredDisenchantingLevel { get; set; }
public float ArmorModifier { get; set; }
public int Duration { get; set; }
public uint[] Spells { get; set; }
#region Custom
public InventorySlotMask InventorySlotMask { get; set; }
public uint RandomSuffixFactor { get; set; }
public uint RequiredSkill { get; set; }
public ItemSubClassMask SubClassMask { get; set; }
/// <summary>
/// Spell to be casted when using this item
/// </summary>
public uint UseSpell { get; set; }
/// <summary>
/// Spell that is casted once and then consumes this Item (usually teaching formulars, patterns, designs etc)
/// </summary>
public uint TeachSpell { get; set; }
/// <summary>
/// Spell to be casted when equipping
/// </summary>
public uint EquipSpell { get; set; }
/// <summary>
/// Spell to be casted when being hit
/// </summary>
public uint HitSpell { get; set; }
/// <summary>
/// Spell to be casted when using Soulstone
/// </summary>
public uint SoulstoneSpell { get; set; }
/// <summary>
/// The ItemSet to which this Item belongs (if any)
/// </summary>
public uint ItemSet { get; set; }
public uint RequiredFaction { get; set; }
public uint RequiredProfession { get; set; }
/// <summary>
/// EquipmentSlots to which this item can be equipped
/// </summary>
public EquipmentSlot[] EquipmentSlots { get; set; }
/// <summary>
/// whether this is ammo
/// </summary>
public bool IsAmmo { get; set; }
/// <summary>
/// whether this is a bag (includes quivers)
/// </summary>
public bool IsBag { get; set; }
/// <summary>
/// whether this is a container (includes chests, clams, bags, quivers, etc.)
/// </summary>
public bool IsContainer { get; set; }
/// <summary>
/// whether this is a key
/// </summary>
public bool IsKey { get; set; }
/// <summary>
/// whether this can be stacked
/// </summary>
public bool IsStackable { get; set; }
/// <summary>
/// whether this is a weapon
/// </summary>
public bool IsWeapon { get; set; }
/// <summary>
/// whether this is a ranged weapon
/// </summary>
public bool IsRangedWeapon { get; set; }
public bool IsThrowable { get; set; }
/// <summary>
/// whether this is a 2h weapon
/// </summary>
public bool IsTwoHandWeapon { get; set; }
/// <summary>
/// whether this teleports one home when using it
/// </summary>
public bool IsHearthStone { get; set; }
/// <summary>
/// The skill needed for this item, for armors, weapons, shields etc
/// </summary>
public SkillId ItemProfession { get; set; }
/// <summary>
/// whether this Item is an equippable Item and not a bag
/// </summary>
public bool IsInventory { get; set; }
/// <summary>
/// The Quest for which this Item needs to be collected
/// </summary>
public uint[] CollectQuests { get; set; }
/// <summary>
/// The Quest that will be started by this Item
/// </summary>
public uint StartQuest { get; set; }
/// <summary>
/// Whether this ItemTemplate has any sockets
/// </summary>
public bool HasSockets { get; set; }
public bool ConsumesAmount;
#endregion
}
So after looking into this, it seems that "ItemCache_GetInfoBlockById" is returning an address which always points to 0. The value at that address minus 0x18 holds the ItemEntry. This can be seen as the function returns.
Code:
pop esi
pop edi
add eax, 18h
pop ebx
pop ebp
retn 14h
There's still no name anywhere to be found near any of these addresses. What gives?
Ah, it seems it is offset +0x1F0 from the value that is returned (not 0x1E4 Apoc, so update your code), and +0x208 from the value that holds the ItemEntry.
Now to just begin reversing all of the ****ing arguments ItemCache_GetInfoBlockById is passed before I start calling it from my code.
/// <summary>
/// Typedef for the virtual DbCache_GetInfoBlockById func
/// </summary>
/// <param name="instance">'this' needs to be the specific cache type pointer</param>
/// <param name="index">The index to search</param>
/// <param name="a3">Pass 0</param>
/// <param name="a4">Pass 0</param>
/// <param name="a5">Pass 0</param>
/// <param name="a6">Pass 0</param>
/// <returns>A pointer to an info block (struct) depending on the type of cache, and function pointer/cache pointer.</returns>
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr GetInfoBlockByIdDelegate(IntPtr instance, int index, int a3, int a4, int a5, int a6);
The last 4 params can be passed 0. (As are passed by 90% of the functions anyway.)
__________________
[Only registered and activated users can see links. ]
/// <summary>
/// Typedef for the virtual DbCache_GetInfoBlockById func
/// </summary>
/// <param name="instance">'this' needs to be the specific cache type pointer</param>
/// <param name="index">The index to search</param>
/// <param name="a3">Pass 0</param>
/// <param name="a4">Pass 0</param>
/// <param name="a5">Pass 0</param>
/// <param name="a6">Pass 0</param>
/// <returns>A pointer to an info block (struct) depending on the type of cache, and function pointer/cache pointer.</returns>
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr GetInfoBlockByIdDelegate(IntPtr instance, int index, int a3, int a4, int a5, int a6);
The last 4 params can be passed 0. (As are passed by 90% of the functions anyway.)
That is strange.. I'll try both I guess. Anyway, way to go with the delegate as a function pointer. That is pure win.
Edit: Is the 'this' value for the item cache 0113EBF0?
Also; I have no idea. I grab the cache pointers at runtime.
I'm curious, are you in-process or out-of-process?
I imagine calling functions from WoW using delegates while out-of-process would require a lot of code magic... in-process seems like it would be easier.
I'm curious, are you in-process or out-of-process?
I imagine calling functions from WoW using delegates while out-of-process would require a lot of code magic... in-process seems like it would be easier.
In-process, if you’re out-of-process, and the project is private. You lose lots of functionality for no good reason.