Dienstag, 9. Dezember 2014

[Reversing / 1.12.1 WoW] Detouring functions

Some time passed and I finally convinced myself to work on the blog again. Using this post I want to continue with the tutorial series having the goal to write a bot for vanilla WoW.

Some Notes:

This time we will focus on injecting assembler code into the WoW executable. Before I start I want to say that I am not the super b reverser and you may find mistakes here and there. Thanks in advance for any hint regarding possible mistakes. If you have problems understanding certain parts I would be happy for a quick message.

Whats the deal about injectin ASM?

If we write bots we have multiple ways to interact with the client. For example we want to target a mob:
We can simulate a tab keypress which will make the client target an attackable mob in line of sight or we can call the function which handles the targeting.

This is just a very basic example but the power of injecting code goes much further: We can start sending packets, call click2move functions, we can even manipulate functions to do things which they arent supposed to do normally.

How does it work?

When we launch WoW the executable is copied into the RAM. The bytes are divided into different segments. In the below example I used IDA to display the segment boundaries of the 1.12.1 WoW executable:

Since Segments arent any important for us at the moment I dont go into detail however you can read up a bit here and there if you like.

Every byte of our target process is accessable via a virtual address (VAS) which can be used in combinisation with a process handle to read from or write to it:

 byte[] res = new byte[6];  
 IntPtr countOfBytesRead = IntPtr.Zero;  
 ReadProcessMemory(WoW.Handle, (IntPtr)0x00837C04, res, 6, out countOfBytesRead);   

This little snippet will call ReadProcessMemory with the handle to our current WoW process aswell the virtual address 0x00837C04. The bytes stored at 0x00837C04 are representing the version string.
Since one ASCII-character takes 1 byte and "1.12.1" consists of 6 characters we have to read 6 bytes:

1.12.1 -> 0x31 0x2e 0x31 0x32 0x2e 0x31
  • 00837C04 holding 0x31
  • 00837C05 holding 0x2e
  • 00837C05 holding 0x31
  • ...
Beside from bytes holding values we also got bytes which are instructions executed by the CPU:

Here we see a function of WoW which we will modify to our needs later. The asm instructions seen at the right of the picture represent the bytes on the left. "PUSH ESI" represents 0x56 for example.

Like in any other programming languages the instructions will be executed one by one from top to the bottom. It looks very complicated at the beginning if you never touched ASM before but playing around with OllyDBG, asking google and reading a few sources will get you going pretty fast (thats atleast what I did).

To modify those instructions there are two solutions I used so far:
  1. Write the bytes for the instructions using WriteProcessMemory
  2. Use a library which accepts ASM instructions, translates them into the corresponding bytes and write them to memory
For the moment I prefer the second method.

About registers and the stack

Before we start you should read up a bit about the stack and registers:

Registers (shown on the upper right) can store 4 byte each. To move a value into them we will use the mov instruction:

 mov eax, 1  ; eax holds the value 1 now

The stack are a bunch of 4 byte values. Using the pop instruction we can move the very top value into a register. Using the push value we can push a register to the top of the stack. Imagine it like a bunch of cards where we can put a new one to the top aswell withdraw the one at the top (Understanding the stack).

Its important that we leave the stack aswell the registers in the state they was before our first injected instruction is executed. Otherwise we have the risk to mess up and crash the process (WoW expects a pointer to a struct in eax and we moved a zero in it without restoring the old eax value for example).


To execute our own instruction we have to take a few steps:
  1. Find a function which is constantly called by WoW (for example directX's EndScene function which produces the image we see while playing -> more info)
  2. Allocate space for our instructions (note down the address of our allocated space)
  3. Inject our instructions at the previously allocated space
  4. Overwrite the first few instruction of EndScene to make a jump to our space
  5. Execute the overwritten instructions followed by our own code
  6. Jump back to EndScene
The whole process a bit simplified:

Instructions stored at address 61 to 66 are injected. Instructions from address 1 to 4 represent a function of WoW which we modify.

Finding EndScene

The EndScene is located at a different address every restart. In conclusion we need to find a way to obtain it each time automatically. I will describe the method I used. Thanks to Namreeb we know that EndScene is called at 0x5A17B6 (give him some rep if you can :p)

  1. So we fire up OllyDBG and attach it to a WoW instance.
  2. Hitting 'CTRL+G' we can jump to any valid address inside the attached process (which is 0x5A17B6 in our case)
  3. As we can see the instruction calling EndScene is "CALL DWORD PTR DS:[ECX+A8]". The logic conclusion is that the address to EndScene is stored at ECX+A8
  4. To retrieve the address we will overwrite the call to endscene with a jmp
  5. Grab the content of ECX+A8
  6. Execute the call we have overwritten
  7. Restore everything to its default state

Since I dont feel like explaining code step by step I wrote a well documentated sample program which will grab the address EndScene is stored at:

Also noteworthy: A jmp takes 5 bytes. The instruction we overwrite in the sample tool takes 6 bytes. After executing our detour we cannot just jump back 5 bytes further and execute a instruction we previously "broke" by modifying parts of it. We need to make sure that we jump to the next full instruction.

If I tell you to start reading the word "hello" at the 3rd character you will end up reading "llo" being confused by what this unknown word should mean to you. I hope you see what I try to say with that :).

Edit from 2017: I sadly lost the source of this example and Filebeam went offline. Stupid me back then should have used GitHub over strange FileHosters :)

Keine Kommentare:

Kommentar veröffentlichen