Home > Games, Reverse Engineering, World of Warcaft > World of Warcraft Morph Hack

World of Warcraft Morph Hack

February 1st, 2009

Today you’re going to learn about how a WoW morph hack works!

Posting this for two reasons:

  1. People cry when things don’t get updated and I’m sick of getting PMs.
  2. On the other hand, they cry anyway so rather than spoonfeed people I’ll give them the means to write their own hack.

Quick side note: Sorry if this post is hard to follow or understand, I’m kinda rushed at the moment. Please leave a comment and let me know if anything doesn’t make sense.

The concept is fairly simple. Every (unit) model has a unique display id. This id (from memory) is stored in WoWs DBC files (which are basically glorified csv files to hold localization info and other data that shouldn’t be hardcoded into the client). When a unit is created, its display id is stored in UNIT_FIELD_DISPLAYID and UNIT_FIELD_NATIVEDISPLAYID. Whenever a unit is ‘morphed’, for example by a spell like Polymorph or Hex, the UNIT_FIELD_DISPLAYID is overwritten with the new display id and the model is redrawn.

Your goal is to replicate the spell morph effect by forcing a new value for the display id and then calling the function to redraw the model. Obviously to do this you will need to find that function. At first this may seem like a difficult task but its actually quite simple.

Note: I’m assuming you already know how to enumerate objects and also get access to an object’s pStorage/descriptor-array/etc (names vary) in order for you to give UNIT_FIELD_DISPLAYID a new value. If you don’t, please consult the WoW Memory Editing forums on MMOwned, there is more than enough information there on the topic.

First you need to work out your starting point. Because morphing is (normally) initiated by the server there would most likely be a packet to perform the morph. If you dig into Mangos/ArcEmu/etc (or if you have a debug client you could use that instead)  and view the opcode list you will see that you are in luck and that the packet SMSG_FORCE_DISPLAY_UPDATE (opcode 0×403) is probably what you want. You can find the packet handler for this by opening WoW in IDA and searching for ‘push 403h’ (which is the opcode being passed to the function which registers the packet handlers).

You will likely get two results for that search.

One like this:

push 403h

call sub_12345678

And another like this:

push <reg>

push offset sub_87654321

push 403h

call sub_23456789

The second is the one you are interested in because you can clearly see it is the one that registers the callback (it takes a function pointer and the opcode), the other one can be discarded (but if you look into it closely you will see it is resetting the opcode in question).

If you follow the function pointer you will be presented with something like the following (the code below is from 3.0.3 - I used an older client so you can’t ‘cheat’):

.text:0067EB00 sub_67EB00      proc near               ; DATA XREF: sub_67F8F0+6E9o


.text:0067EB00 var_8           = dword ptr -8

.text:0067EB00 var_4           = dword ptr -4

.text:0067EB00 arg_C           = dword ptr  14h


.text:0067EB00                 push    ebp

.text:0067EB01                 mov     ebp, esp

.text:0067EB03                 mov     ecx, [ebp+arg_C]

.text:0067EB06                 sub     esp, 8

.text:0067EB09                 lea     eax, [ebp+var_8]

.text:0067EB0C                 push    eax

.text:0067EB0D                 push    ecx

.text:0067EB0E                 call    sub_69FE30

.text:0067EB13                 mov     edx, [ebp+var_4]

.text:0067EB16                 mov     eax, [ebp+var_8]

.text:0067EB19                 push    65Fh

.text:0067EB1E                 push    offset a_Unit_c_cpp ; “.\\Unit_C.cpp”

.text:0067EB23                 push    8

.text:0067EB25                 push    edx

.text:0067EB26                 push    eax

.text:0067EB27                 call    sub_46D3C0

.text:0067EB2C                 add     esp, 1Ch

.text:0067EB2F                 test    eax, eax

.text:0067EB31                 jz      short loc_67EB3E

.text:0067EB33                 push    1

.text:0067EB35                 push    1

.text:0067EB37                 mov     ecx, eax

.text:0067EB39                 call    sub_67B2E0


.text:0067EB3E loc_67EB3E:                             ; CODE XREF: sub_67EB00+31j

.text:0067EB3E                 mov     eax, 1

.text:0067EB43                 mov     esp, ebp

.text:0067EB45                 pop     ebp

.text:0067EB46                 retn

.text:0067EB46 sub_67EB00      endp

If you read the function you can see it takes the packet data and does some processing with it (in sub_69FE30) which returns two unsigned ints via pointers to local variables. These variables are then passed to a second function. Because the morph is done on a single unit we can likely guess that that the function that performs the morphing is a member function of CGUnit_C (the class used to represent units in WoW). Based on this we can see that sub_67B2E0 is the function we want. sub_46D3C0 takes the two unsigned ints (along with some other things) and its return value is passed to sub_67B2E0 through ecx, this means that sub_67B2E0 is a class member function which confirms our suspicions about it being a member of CGUnit_C. We can now see that the two unsigned ints are actually the GUID of the unit that the game is going to morph and that the return value of sub_46D3C0 is the pointer to the unit which corresponds to that guid.

With this information we can now get the prototype and address for the function we need to call to redraw the object with its new display id. We know its done on units (just based on regular gameplay, but you can also tell from the string that is passed to the function which grabs the object pointer via guid), we also know the params we need to pass (we can just rip them right from the packet handler, we don’t really need to know or care what they’re actually for). From this we can derive:

void CGUnit_C::UpdateModel(unsigned int Unk1 = 1, unsigned int Unk2 = 1); // Address = 0×006800A0

It would be even more obvious what was going on if we looked inside the functions being called by the packet handler but its so simple that we don’t really need to.

Exercise for the reader: Find the function for the latest patch, and for bonus points, write a simple morph hack for the latest patch.

Pop quiz: You are the only person who can see the changed model. Why is this?

Cypher Games, Reverse Engineering, World of Warcaft , ,

  1. sku
    February 1st, 2009 at 23:06 | #1

    Very nice post, thanks alot. So.. where do I collect my points?

  2. February 1st, 2009 at 23:13 | #2

    Please send an email to [email protected].
    They’ll happily help you.

  3. Robske007a
    February 3rd, 2009 at 08:07 | #3

    Nice guide indeed,

    So here’s some traffic

  4. jjaa
    February 3rd, 2009 at 08:55 | #4

    Hey cypher, Nice work as usual

    Also going with the whole cyphermorph theme why not do console commands next :D. Just a suggestion, i am sure whatever you pick it will be great

  5. February 3rd, 2009 at 11:33 | #5

    Nice idea. I might just do that.

  6. luciferc
    February 3rd, 2009 at 14:23 | #6

    Nice, maybe next time do an intro on object structure on wow since alot of people seem to ask about that in the forums, or how to make a simple fish bot would be a cool intro for people.

  1. No trackbacks yet.