
We are writing a bot for MMORPG with assembler and draenei. Part 1
- Tutorial

But first things first, so I'm waiting for you under the cut!
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 MMORPG to help them deal with bots. And, of course, the author of the article is not a bot driver, not a cheater, and he never was.
As you remember from the last article, we found the address of our DirectX function EndScene and read the first 5 bytes of it. If you have forgotten, then here is the content, if not, then read what we will do with them:
Content
- Part 0 - Finding a code injection point
- Part 1 - Implementation and execution of third-party code
- Part 2 - Hide the code from prying eyes
- Part 3 - Under the Sight of World of Warcraft 5.4.x (Structures)
- Part 4 - Under the Sight of World of Warcraft 5.4.x (Moving)
- Part 5 - Under the Sight of World of Warcraft 5.4.x (Custom Fireball)
1. We outline the implementation plan
Today we will embed our code in this function without prejudice to itself. Below I will show how this will happen:

- HookAddress is the address to the allocated memory during the game using the WinApi VirtualAllocEx function from kernel32.dll
- Address is the address in the DirectX memory of the EndScene or ChainSwap function
- OpCodes are the original function opcodes and we need to save them, because in the original they will be changed.
2. Implementation operation
To open the process, we call WinApi OpenProcess and enable debugging, and then we need to open the main thread of our process
var ProcessHandle = OpenProcess(processId);
Process.EnterDebugMode();
var dwThreadId = Process.GetProcessById(dwProcessId).Threads[0].Id;
var ThreadHandle = OpenThread(0x1F03FF, false, (uint)dwThreadId);
var HookAddress = Memory.AllocateMemory(6000);
var argumentAddress1 = Memory.AllocateMemory(80);
Memory.WriteBytes(argumentAddress1, new byte[80]);
var argumentAddress2 = Memory.AllocateMemory(BufferSize);
Memory.WriteBytes(argumentAddress2, new byte[80]);
var resultAddress = Memory.AllocateMemory(4);
Memory.Write(_resultAddress, 0);
where 0x1F03FF - access rights to the stream. Next, we allocate memory for our code and get a HookAddress pointer to it , also reserve memory for the two arguments argumentAddress1 and argumentAddress2, for the result resultAddress and fill it with zeros. Now, as promised a bit of hardcore:
var asmLine = new List {
"pushfd",
"pushad",
"mov edx, 0",
"mov ecx, " + resultAddress,
"mov [ecx], edx",
"@loop:",
"mov eax, [ecx]",
"cmp eax, " + 80,
"jae @end",
"mov eax, " + argumentAddress1,
"add eax, [ecx]",
"mov eax, [eax]",
"test eax, eax",
"je @out",
"call eax",
"mov ecx, " + resultAddress,
"mov edx, " + argumentAddress2,
"add edx, [ecx]",
"mov [edx], eax",
"mov edx, " + argumentAddress1,
"add edx, [ecx]",
"mov eax, 0",
"mov [edx], eax",
"@out:",
"mov eax, [ecx]",
"add eax, 4",
"mov [ecx], eax",
"jmp @loop",
"@end:",
"popad",
"popfd"
};
Memory.Asm = new ManagedFasm(ProcessHandle);
Memory.Asm.Clear();
foreach (var str in asmLine)
{
Memory.Asm.AddLine(str);
}
Memory.Asm.Inject(HookAddress);
var length = (uint) Memory.Asm.Assemble().Length;
Memory.WriteBytes(HookAddress + length, OpCodes);
Memory.Asm.Clear();
Memory.Asm.AddLine("jmp " + (Address + OpCodes.Length));
Memory.Asm.Inject((uint)((HookAddress + length) + OpCodes.Length));
Memory.Asm.Clear();
Memory.Asm.AddLine("jmp " + HookAddress);
for (var k = 0; k <= ((OpCodes.Length - 5) - 1); k++)
{
Memory.Asm.AddLine("nop");
}
Memory.Asm.Inject(Address);
The assembler code above is written to HookAddress and will transfer control to our code, and according to the table, after it is processed, we return control to the main thread. Now I will show how to use it, let us have:
public byte[] InjectAndExecute(IEnumerable asm, bool returnValue = false, int returnLength = 0)
{
Memory.Asm.Clear();
foreach (var str in asm)
{
Memory.Asm.AddLine(str);
}
dwAddress = Memory.AllocateMemory(Memory.Asm.Assemble().Length + 60);
Memory.Asm.Inject(dwAddress);
Memory.Write(argumentAddress1, dwAddress);
while (Memory.Read(argumentAddress1) > 0)
{
Thread.Sleep(1);
}
byte[] result = new byte[0];
if (returnValue)
{
result = Memory.ReadBytes(Memory.Read(argumentAddress2), returnLength);
}
Memory.Write(argumentAddress2, 0);
Memory.FreeMemory(dwAddress);
return result;
}
As a result, we have the values at the addresses argumentAddress1 and argumentAddress2 should become zeros when our injection works. If you have many threads that call InjectAndExecute, then you need to provide a queue, for this I used 80 bytes in size, how to implement it, think for yourself. And in the next article, I will show my implementation and how to hide our code.