Header Image

Roman Hergenreder

Computer Science Student & Software Developer

Unlocking API-Functions in WoW 3.3.5a using a Disassembler

This article deals with hacking a the World of Warcraft binary, modifying it to give access to lua api functions, which could only be used by integrated Blizzard addons.

Update: The method described here is not optimal, but shows clearly, how it works in general. Last modified: 2020-07-14 17:05:36

This article deals with unlocking specific LUA-API functions provided by World of Warcraft, especially for version 3.3.5a. It is possible, that this method would also work on other versions, but I haven't tested it, so you have to go on yourself. Most of the API is well documented here: http://wowwiki.wikia.com/wiki/World_of_Warcraft_API. As you can see, there are several functions, which are marked as PROTECTED or HW, which means, that a hardware event like an user input is required. To be more precisely, PROTECTED functions cannot be run at all by an user, HW functions only by using macros or a direct command in chat (/run or /script). Below I'm going to show you, how to bypass these restrictions.

All you need is listed below:
  • WoW-Client and Server on 3.3.5a, where you can test your stuff
  • Disassembler with Debugging Options (I'm using IDA Pro)
  • DIF-Patcher (I've compiled this program)
  • Any Editor for lua code
There are actually multiple ways to patch your changes into the binary, you just have to try them out.

The first step is diassembling the Wow binary. I'll show you the steps under IDA Pro, if you are using a different diassembler, you have to reproduce the steps on your own. So firstly, you have to run IDA Pro on 32-Bit mode, as the 3.3.5a Client does not exist for 64-Bit. Click on 'New', select your Wow.exe and another dialog should appear. You don't have to change very much, just make sure 'MetaPC' is selected as Processor type, so IDA is trying to disassemble all possible opcodes. The disassembler loops through the binary for multiple times, to detect references, so you have to wait a few minutes.
ida_load_new_file.jpg
Let's think about, what we are going to do next. The following steps are based on unlocking the function AcceptBattlefieldPort, the steps should be similar to unlock other functions. So firstly, we have to ask ourselves, what happens inside the WoW executable, when we run any LUA-Function. Blizzard is using the LUA-API from lua.org (when you disassemble it, you will notice the copyright). There is one function in general, which connect LUA-Functions with C/C++ Functions: luaL_register(lua_State*, const char*, lua_CFunction). All we need to know is, that a compiled procedure (actually the address/reference) is called by a lua function name. So all we need to do is finding this string in the binary.
In IDA open the String-Table (View => Open Subviews => Strings or press Shift+F12) and search for our function "AcceptBattlefieldPort" (CTRL-F). By doubleclicking the result, you will get into the data segment.
ida_strings_view.jpg
Right next to this entry, you should see a comment ;; X-REF. If you don't see this comment, the disassembler haven't finsished loading the binary. The X-Ref tells us, where the string is used inside the code. Doubleclicking it again, we will be forwarded to another part in the data segment, where string names and function addresses are listed.
ida_view_a_1.jpg
The line below our string entry is the function, which will be called, if we run this lua function. Doubleclicking it, we will see the assembler instructions.
ida_view_a_2.jpg
Now it gets more complicated. We see alot of instructions but we don't know what we are looking for yet. The basic idea is stopping the client to call the RET (return) instruction, we don't know the original C++ source code, but we can guess, it will look like this:
C++
void AcceptBattlefieldPort(lua_State *L) {
  int n = lua_gettop(L);
  if(n == 0) {
    luaL_error("Usage: Usage: AcceptBattlefieldPort(index, accept)\n");
    return;
  }

  if(ThereIsAHardwareEvent()) {
    doStuff();
  }

  return;
}
ASM
sub_AcceptBattleFieldPort proc near
  ;; any instructions here
  call  ThereIsAHardwareEvent
  test  eax, eax   ;; or cmp eax,0
  jnz   doStuff    ;; or maybe jz, jb, ...?
  retn             ;; else finish

We should have found the function, which is executed. Our idea is to modify the program flow by changing the jump instructions or deleting them (replacing with NOP). All we need to do now is finding the right jump instructions. To do that, we need to set a breakpoint to the beginning of the function and starting the debugger. Once the client is started we have to compare both ways: Calling the function directly using:

/run AcceptBattlefieldPort(1,1)

and without an hardware event, e.g. using the update loop:

/run local f=CreateFrame("frame");f:SetScript("OnUpdate",(function(self, elapsed) AcceptBattlefieldPort(1,1) end))
Now we have to look, at which point the two function calls take a different way. I recommend using the Graphview (right click => Graphview). There you can see, that the second jump instructions interrupts the function, if it is not a hardware event (at .text:0054DA78). All we need to do is replacing these instructions with NOPs. On the menubar press Edit => Patch program => Assemble.. and enter nop two times (because jz <addr> takes two bytes space).
ida_view_a_3.jpg
ida_view_a_4.jpg

We have to apply our changes to the binary. First, we need to create a DIF-File. It contains every byte we changed in a readable format. In the menubar, press File > Produce File.. > Create DIF-File. Now you run the difpatcher by issuing this command (should be similar to Windows or OSX): difpatcher -i Wow.exe -p <Your DIF File>

You may test, if your patching was done correctly. If the Wow.exe crashs or the API-Function still doesn't work, you possibly changed the wrong bytes. If it was successfull, you could write an Addon, which automatically joins a battleground (I already wrote a minimalistic example addon which can be found here). With this method, you should also be able to unlock every function, especially the interaction functions like moving, casting spells & stuff, which allows you to write on bots. But please, be always honest and don't use this hacky stuff to have an advantage against other players :).