📜 ⬆️ ⬇️

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

Hi% username%! Having rummaged in articles of Habr, I found some these about writing of 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 mock mobs or ore on a given route, killing aggressive mobs, players and everyone who attacks you on the way, shouting obscenity after them, and what else they couldn’t determine. In general, the full emulation of the average MMORPG player. Writing macros for AutoIt, simulating clicks in the window, analyzing pixels under the cursor is not at all our option. Intrigued? Welcome under the cut!
Disclaimer: The author is not responsible for your use of the knowledge gained in this article or damage as a result of their use. All information here is for educational purposes only. Especially for companies developing MMORPG, to help them fight with the bot. And, of course, the author of the article is not a botmaster, not a cheater, and never was.


Content

  1. Part 0 - Search for a code injection point
  2. Part 1 - Implementing and executing third-party code
  3. Part 2 - Hide the code from prying eyes
  4. Part 3 - Under the gun World of Warcraft 5.4.x (Structures)
  5. Part 4 - Under the gun World of Warcraft 5.4.x (Moving)
  6. Part 5 - Under the gun World of Warcraft 5.4.x (Casting Fireball)

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

Definitely we need to embed the code in the game process, which will manage it. For this, you can modify the executable file itself (it is very easy to do, but it is also easy to identify and get banned) or to inject a DLL (this is also determined very simply), 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, which will not be so obvious for anti-cheats and useful for us. There may be a lot of such points, but for many reasons, the best solution would be to introduce the game into the drawing, i.e. create hook for Direct3D. Again, for many reasons, it is best to intercept the EndScene function, because before calling it, all changes in the game world and other calculations will already occur. Here is an ongoing process inside for clarity:
  1. ...
  2. Drawing objects of the current game scene
  3. Calling out a swapped D3D EndScene
  4. Our code
  5. Call 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 with the frequency of your fps.
Note: fps can be quite a high value, so you should not handle every code call. I think 10-15 calls per second will be enough


2. Toolkit

So we have a plan, now we need tools. I (like most hope) love to use everything ready. By this I propose to acquire the following things:
  1. Any IDE where we will write C # code
  2. IDA - in my opinion the best debager
  3. HackCalc - a calculator for recalculating VA (virtual address) to Offset and back
  4. SlimDX - DirectX framework for .NET
  5. FlatAsm Managed - the library converts assembler mnemonic codes into bytecode



3. Search for the implementation point

So, we figured out the way, got the tools, now we need to understand where to implement our code.
When drawing with D3D, a virtual object Direct3D device is created, which is essentially a VMT (virtual method table, which is a pointer to a pointer to a D3D method table). This table stores, again, pointers to Direct3D methods, such as BeginScene, EndScene, DrawText, etc. In this case, we are only interested in EndScene, since If the Direct3D device is created in a single copy, 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 this problem can be solved by a simple search. For this we will use SlimDX.
In the processMemory.Read and processMemory.ReadBytes code, 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<uint>(processMemory.Read<uint>((uint)(int)device.ComPointer) + 0xa8); //   _D3D9OpCode = (int)processMemory.Read<byte>(_D3D9Adress) != 0x6a ? processMemory.ReadBytes(_D3D9Adress, 5) : processMemory.ReadBytes(_D3D9Adress, 7); } 

Where does 0xA8 come from? .. If you open the d3d9.h and find the EndScene method, then you will find
 STDMETHOD(EndScene)(THIS) PURE; 
The 42nd in the account 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 has crashed, then we need to try the D3D11 and everything is a bit more complicated, we do not need 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<uint>(processMemory.Read<uint>((uint)(int)swapChain.ComPointer) + 0x20); _D3D11OpCode = processMemory.ReadBytes(_D3D11Adress, 5); } } 

All this again needs to be wrapped again in try / catch. In the case of both attempts failing to get the address of the D3D function, the offsets may have changed or you may not have D3D9 or D3D11.
Summary : We have the address of the EndScene D3D function and the opcodes of this function.
What to do with them and how to implement my code, I will probably tell in the following parts, but for now vote, interpret the code above, google, Yandex, bing, yahu ..., ask in comments and read the assembler documentation, there will be full hardcore and draeney!

')

Source: https://habr.com/ru/post/251137/


All Articles