WoW’s Console
Hey, as requested I’m gonna post the info needed to use WoW’s console in a hack. Because I’m a little short on time I’m just going to post the bare minimum, but at the end of the post I’ll point out some of the other functions that may be of interest in case you want to go hunting for them.
Note: I’m assuming you already know how to enable and use WoW’s console. (Hint: ‘WoW.exe -console’, then use the ‘`’ key to bring it down. Just like in most other games.)
First off, registering console commands. The easiest way to find the function needed to do this is look for a known command in the binary. I chose ‘worldport’ because I’ve used it before, and found this:
.text:004073B3 push 0 ; int
.text:004073B5 push 0 ; int
.text:004073B7 push offset sub_406D70 ; int
.text:004073BC push offset aWorldport ; “worldport”
.text:004073C1 call sub_6A0BD0
Now, from that we can easily see that sub_6A0BD0 is the function we are interested in because it takes both the command name and the associated function pointer (which you can verify by looking inside). Based on that the callback registration function prototype will be the following:
void __cdecl RegisterConsoleCommand(unsigned int Unk1 = 0, unsigned int Unk2 = 0, void* pCallback, const char* Command);
What you will notice though is that if you try to use your new command WoW will exit. Why is that? Well, if you go to the trouble of debugging it (Hint: start at ConsoleExec, called by the Lua subroutine by the same name) you will come across the following function:
.text:007CB3A0 sub_7CB3A0 proc near ; CODE XREF: sub_69F470+B9p
.text:007CB3A0 ; sub_6A11A0+1Ap …
.text:007CB3A0
.text:007CB3A0 var_40 = byte ptr -40h
.text:007CB3A0 arg_0 = dword ptr 8
.text:007CB3A0
.text:007CB3A0 push ebp
.text:007CB3A1 mov ebp, esp
.text:007CB3A3 mov eax, dword_12EA640
.text:007CB3A8 mov ecx, dword_12EA644
.text:007CB3AE sub esp, 40h
.text:007CB3B1 test eax, eax
.text:007CB3B3 jz short loc_7CB3B9
.text:007CB3B5 test ecx, ecx
.text:007CB3B7 jnz short loc_7CB3C9
.text:007CB3B9
.text:007CB3B9 loc_7CB3B9: ; CODE XREF: sub_7CB3A0+13j
.text:007CB3B9 call sub_7CB310 ; <”Unable to find .text section”, “.text”>
.text:007CB3BE mov eax, dword_12EA640
.text:007CB3C3 mov ecx, dword_12EA644
.text:007CB3C9
.text:007CB3C9 loc_7CB3C9: ; CODE XREF: sub_7CB3A0+17j
.text:007CB3C9 mov edx, [ebp+arg_0]
.text:007CB3CC cmp edx, eax
.text:007CB3CE jb short loc_7CB3D4
.text:007CB3D0 cmp edx, ecx
.text:007CB3D2 jb short loc_7CB3F1
.text:007CB3D4
.text:007CB3D4 loc_7CB3D4: ; CODE XREF: sub_7CB3A0+2Ej
.text:007CB3D4 push edx
.text:007CB3D5 push offset aInvalidFunctio ; “Invalid function pointer: %p”
.text:007CB3DA lea eax, [ebp+var_40]
.text:007CB3DD push 40h
.text:007CB3DF push eax ; char
.text:007CB3E0 call sub_6A6920
.text:007CB3E5 lea ecx, [ebp+var_40]
.text:007CB3E8 push ecx ; char *
.text:007CB3E9 call sub_6A9E60
.text:007CB3EE ; —————————————————————————
.text:007CB3EE add esp, 14h
.text:007CB3F1
.text:007CB3F1 loc_7CB3F1: ; CODE XREF: sub_7CB3A0+32j
.text:007CB3F1 mov esp, ebp
.text:007CB3F3 pop ebp
.text:007CB3F4 retn
.text:007CB3F4 sub_7CB3A0 endp
Again, if you go to the trouble of reversing this function (which I’m not going to explain in detail because its beside the point of this post) you will see that it is looking at WoWs PE header, getting the start and end of WoW’s ‘.text’ segment (which is where all code is kept) and ensuring no function pointers are being registered outside that range. If a function pointer outside the valid range is found then WoW will quit.
What is the easiest way to get around this? Hook the function to just do a “return” at the start. If you want to just write a single byte to do the same (even easier), simply write ‘0xC3′ to the top of the function. Please keep in mind this is ignoring the presence of Warden. If you want to ensure you don’t get detected by Warden you will need to breakpoint whatever you’re hooking to ensure it’s not being scanned, or use a generic Warden bypass.
Now with that out of the way we move to printing output. Funnily enough we can backtrack to the exact same starting position as before:
.text:004073B3 push 0 ; int
.text:004073B5 push 0 ; int
.text:004073B7 push offset sub_406D70 ; int
.text:004073BC push offset aWorldport ; “worldport”
.text:004073C1 call sub_6A0BD0
I checked worldport and happen to know that if you pass it invalid params it will print usage information to the console. Knowing that we can dive into worldport’s callback and we find this:
.text:00406DED push 4
.text:00406DEF push offset aUsageWorldport ; “Usage: worldport <continentID> [x y z] “…
.text:00406DF4 call sub_69D850 ; <”.\ConsoleClient.cpp”, “.?AUCONSOLELINE@@”>
.text:00406DF9 add esp, 8
Again, very obvious what’s going on there.
void __cdecl ConsolePrint(unsigned int Unk1 = 0, const char* Output);
Using that you can easily write output to the console.
Well, that pretty much sums up the pure basics. If people are interested in some of the other console related stuff (changing the print colour, registering cvars, modifying/retrieving cvar values, what the parameters we’re ignoring are for, etc.) let me know. If you’d rather see something different like packet-based stuff, or even something non WoW related like my new usermode rootkit project or something else related to Windows reversing and internals feel free to suggest that too.
Extra Credit:
See if you can work out what the extra parameters I’ve marked as “Unk” (unknown) in each function are for. I’ll let you know if you’re right or wrong.