We are writing a bot for MMORPG with assembler and draenei. Part 0

  • Tutorial
Hi% username%! Having rummaged in articles of a habr, I found some of them about writing bots for MMORPG. Undoubtedly these are very interesting and informative articles, but the possibilities in them are very scarce. What if, for example, you need to farm mobs or ore along a given route, killing aggressive mobs, players and everyone who will attack you along the way, shouting obscenities after them, but they still could not be determined. In general, a complete emulation of the average player MMORPG. Writing macros for AutoIt, simulating clicks in a window, analyzing pixels under the cursor is not our option at all. Intrigued? Welcome to cat!
Disclaimer: The author is not responsible for your use of the knowledge gained in this article or damage resulting from their use. All information presented here is for educational purposes only. Especially for companies developing MMORPGs to help them deal with bot drivers. And, of course, the author of the article is not a bot driver, not a cheater, and he never was.


Content

  1. Part 0 - Finding a code injection point
  2. Part 1 - Implementation and execution of third-party code
  3. Part 2 - Hide the code from prying eyes
  4. Part 3 - Under the Sight of World of Warcraft 5.4.x (Structures)
  5. Part 4 - Under the Sight of World of Warcraft 5.4.x (Moving)
  6. Part 5 - Under the Sight of World of Warcraft 5.4.x (Custom Fireball)

So right off the bat.
1. The choice of implementation implementation method (a bit of theory)

Definitely, we need to implement the code in the game process, which will control it. For this, you can modify the executable file itself (it is very easy to do, but it is also easy to determine and get a ban) or implement a DLL (it is also very easy to determine), but this is not for us. Our approach is to inject code into the main thread of a process that receives control and returns it back.
To do this, you need to find / come up with an implementation point that will not be so obvious for anti-cheats and useful for us. There can be a lot of such points, but for many reasons, the best solution would be to introduce the game into rendering, i.e. creating a hook for Direct3D. Again, for many reasons, it is best to intercept the EndScene function, because before it is called, all changes to the game world and other calculations will already occur. Here's the process inside for clarity:
  1. ...
  2. Drawing objects of the current game scene
  3. Call spoofed D3D EndScene
  4. Our code
  5. Calling the original D3D EndScene
  6. Next scene
  7. ...

The scene in this key is the so-called frame. In other words, our code will work at the frequency of your fps.
Note: fps can be a fairly high value, so you should not process every call to the code. I think it will be enough 10-15 calls per second


2. Toolkit

So we have outlined a plan, now we need tools. I (like most hope) love to use everything ready-made. Accordingly, I propose to acquire the following things:
  1. Any IDE where we will write code in C #
  2. IDA - in my opinion the best debugger
  3. HackCalc - calculator for translating VA (virtual address) into Offset and vice versa
  4. SlimDX - DirectX Framework for .NET
  5. FlatAsm Managed - library converts assembly mnemonics to bytecode



3. Search for an implementation point

So we figured out the method, got the tools, now we need to understand where to embed our code.
When rendering using D3D, a virtual Direct3D device object is created, which is essentially a VMT (virtual method table, which is a pointer to a pointer to the D3D method table). This table, again, stores pointers to Direct3D methods, such as BeginScene, EndScene, DrawText, etc. In this case, we are only interested in EndScene, because Direct3D device is created in a single instance, then we need to get a pointer to it, and then get pointers to the table. Again, we need to determine which D3D is used in the game client, and since we have 2 options (DX9 and DX11), then you can solve this problem by a simple exhaustive search. For this we will use SlimDX.
In the code processMemory.Read and processMemory.ReadBytes, the wrappers of the standard ReadProcessMemory from kernel32.dll
check for DX9:
//Создаем устройство D3D9
var device = new SlimDX.Direct3D9.Device(
    new SlimDX.Direct3D9.Direct3D(), 
    0, 
    DeviceType.Hardware, 
    Process.GetCurrentProcess().MainWindowHandle, 
    CreateFlags.HardwareVertexProcessing, 
    new[] { new PresentParameters() });
    using (device)
    {
//Открываем текущий процесс
        var processMemory = new ProcessMemory((uint)Process.GetCurrentProcess().Id);
//Считываем необходимый нам адрес расположения в памяти D3D9 функции по смещению 0xA8 от указателя на Com объект
        _D3D9Adress = processMemory.Read(processMemory.Read((uint)(int)device.ComPointer) + 0xa8);
//Считываем опкоды функции
        _D3D9OpCode = (int)processMemory.Read(_D3D9Adress) != 0x6a ? processMemory.ReadBytes(_D3D9Adress, 5) : processMemory.ReadBytes(_D3D9Adress, 7);
    }

Where does 0xA8 come from? .. If you open d3d9.h and find the EndScene method, then you will find
STDMETHOD(EndScene)(THIS) PURE;
42nd in a row in the IDirect3DDevice9 interface, and one function in the x86 architecture is addressed by 4 bytes, we get 42 * 4 = 168, and this is 0xA8.
All this needs to be wrapped in try / catch and if an error occurs, then we need to try D3D11 and everything is a little more complicated, we need not EndScene, but SwapChain, it is located at index 8, i.e. 8 * 4 = 32 = 0x20:
//Создаем форму отрисовки для получения устройства D3D11
using (var renderForm = new RenderForm())
{
    var description = new SwapChainDescription()
    {
        BufferCount = 1,
        Flags = SwapChainFlags.None,
        IsWindowed = true,
        ModeDescription = new ModeDescription(100, 100, new Rational(60, 1), SlimDX.DXGI.Format.R8G8B8A8_UNorm),
        OutputHandle = renderForm.Handle,
        SampleDescription = new SampleDescription(1, 0),
        SwapEffect = SlimDX.DXGI.SwapEffect.Discard,
        Usage = SlimDX.DXGI.Usage.RenderTargetOutput
    };
    SlimDX.Direct3D11.Device device;
    SlimDX.DXGI.SwapChain swapChain;
    var result = SlimDX.Direct3D11.Device.CreateWithSwapChain(
        DriverType.Hardware, 
        DeviceCreationFlags.None, 
        description, 
//Здесь мы получаем устройство
        out device, 
        out swapChain);
    if (result.IsSuccess) using (device) using (swapChain)
    {
//И открыв текущий процесс - считаем адрес функции и опкоды
        var processMemory = new ProcessMemory((uint)Process.GetCurrentProcess().Id);
//Считываем наш SwapChain
        _D3D11Adress = processMemory.Read(processMemory.Read((uint)(int)swapChain.ComPointer) + 0x20);
        _D3D11OpCode = processMemory.ReadBytes(_D3D11Adress, 5);
    }
}

All this again needs to be wrapped again in try / catch. If both attempts to get the address of the D3D function fail, the offsets may have changed or you do not have D3D9 or D3D11.
Bottom line : We have the address of the EndScene D3D function and the opcodes of this function.
What can I do with them and how to implement my code, I can tell you in the following parts, but for now, vote, comprehend the code above, google, Yandex, binge, yahoo ..., ask in the comments and read the assembler documentation, there will be full hardcore and draenei!

Only registered users can participate in the survey. Please come in.

Write a sequel?

  • 95.3% Yes 1734
  • 4.6% No 84

Also popular now: