WoW Memory EditingWoW Memory Editing for learning purposes only.
This section is more advanced than others on MMOwnedRead the section specific rules, infractions will be given out if u break them!That is including the expectations! - If you don't meet them then don't post
Truth be told, ever since WoW!Sharp, I haven't hacked/disassembled/whatever WoW in any way, shape, or form (except for writing a teleporter for an EMU server, big whoop). I've never worked with TLS--nor am I even really sure what it stands for, to be honest--before today, but I've kinda decided that I want to try to catch up with the whole scene.
I've read almost every thread I could get my hands on over the past 24-48 hours, and have compiled and run many, many different PoCs or examples. From what I can tell, the first step in making a bot--or anything, really--is to find the s_curMgr, which allows you to loop through loaded objects (including your character?). I've seen people left in the dust when their "TLS Pointer" is outdated due to client updates, so I set out to solve that problem--or, at the very least, make it easier to solve. Enter: dom1n1k's dwFindPattern (external C# version).
([Only registered and activated users can see links. ])
Code:
using System;
using System.Collections.Generic;
using System.Text;
using MemoryLib;
namespace ExternalFindPattern
{
class Program
{
static void Main(string[] args)
{
DateTime now = DateTime.Now; //used for testing how long it takes to find the tls pointer
System.Diagnostics.Process.EnterDebugMode(); //gives our program debug permissions
//open wow for read/write
IntPtr hProcess = Memory.OpenProcess(Memory.GetProcessIdByProcessName("wow.exe"));
//if open process was successful
if (hProcess != IntPtr.Zero)
{
//search for the code pattern that we want (in this case, WoW TLS)
uint tlscode = dwFindPattern(hProcess, 0x410000, 0x400000,
"EB 02 33 00 64 8B 15 2C 00 00 00 8B 0D 00 00 00 00 8B 0C 8A",
"xxx?xxxxxxxxx????xxx");
//read Kynox's g_clientConnection from memory
uint g_clientConnection = Memory.ReadUInt(hProcess, Memory.ReadUInt(hProcess, (tlscode + 0x16)));
//first, the offset for the curMgr inside g_clientConnection is read,
//then s_curMgr is read from g_clientConnection + that offset (which may change version to version,
//I honestly don't know)
uint s_curMgr = Memory.ReadUInt(hProcess, (g_clientConnection + Memory.ReadInt(hProcess, tlscode + 0x22)));
//output to console
Console.WriteLine("TLS code: 0x{0:X08}ng_clientConnection: 0x{1:X08}ns_curMgr: 0x{2:X08}", tlscode, g_clientConnection, s_curMgr);
Memory.CloseHandle(hProcess);
}
//tell user how long it took to find and get what we wanted
TimeSpan timer = DateTime.Now.Subtract(now);
Console.WriteLine("nnTime taken: {0}msnnPlease press [ENTER] to continue...", timer.Milliseconds);
Console.ReadLine();
}
#region dwFindPattern
//blatantly adapted/copied from dom1n1k :)
static bool bDataCompare(byte[] data, int index, byte[] pattern, string mask)
{
if (pattern.Length != mask.Length) return false;
for (int i = 0; i < pattern.Length; i++)
if (mask[i] == 'x' && (data[index + i] != pattern[i]))
return false;
return true;
}
//blatantly adapted/copied from dom1n1k :)
static uint dwFindPattern(IntPtr hProcess, uint start, int length, string _pattern, string mask, char delimiter)
{
string[] p = _pattern.Split(delimiter);
byte[] pattern = new byte[p.Length];
for (int i = 0; i < p.Length; i++)
pattern[i] = Convert.ToByte(p[i], 16);
const int bytestoread = 1024;
int index = 0;
byte[] buf;
if (bytestoread > length)
{
buf = new byte[length];
Memory.ReadMemory(hProcess, start, ref buf);
for (int i = 0; i < (buf.Length - pattern.Length); i++)
if (bDataCompare(buf, i, pattern, mask))
return (uint)(start + i);
}
else
{
while (index < length)
{
buf = new byte[bytestoread + pattern.Length];
Memory.ReadMemory(hProcess, start + index, ref buf);
for (int i = 0; i < bytestoread; i++)
if (bDataCompare(buf, i, pattern, mask))
return (uint)(start + index + i);
index += bytestoread;
}
}
return uint.MaxValue;
}
static uint dwFindPattern(IntPtr hProcess, uint start, int length, string _pattern, string mask)
{
return dwFindPattern(hProcess, start, length, _pattern, mask, ' ');
}
#endregion
}
}
Now then, this serves two purposes:
Gives a program the ability to look for patterns (useful for making your program either update itself or completely ignore client version) without injecting anything into the client.
Finds Kynox's g_clientConnection and, subsequently, the s_curMgr externally, allowing the program to loop through loaded objects, regardless (hopefully?) of client version.
In order to use this in the leechtastic, copy+paste way, you'll need my MemoryLib class library, located [Only registered and activated users can see links. ]. Source is included, though it's a bit rough around the edges and nowhere near finished (I've worked on it for a few hours here or there for nearly a year).
Kynox, Bobbysing, UnknOwned: you guys have been doing this longer than I have, so, please, point out what I'm doing wrong, what I could be doing better, and/or any suggestions you might have, up to and including what should be my next step, in your opinion, towards a bot. I've got a few ideas of my own, but I'm really in over my head with information, at the moment. If you wouldn't mind helping me learn what it is I need to learn, I'd love the opportunity to pick your brain(s) about certain things. Let me know.
Anyway, there's my first contribution with (hopefully) more to come as I learn and feel more comfortable. See the second post in this thread for a better explanation of what's going on in my code.
CREDITS:
dom1n1k
Kynox
bobbysing
Anyone else that I forgot: I'm sorry. I've read a lot of threads and gleaned a lot of information from many different sources. Please, let me know if I've forgotten anyone.
Last edited by Shynd; 05-31-2008 at 02:02 PM.
Reason: added credits
Donate to remove ads, get your "DONATOR title, and get access to the MMOwned community's elite Shoutbawx.
This searches for what I've determined to be the easiest portion of WoW game code from which to glean the largest amount of information. In 2.4.2, it looks a bit like this:
Line 21 should find the pattern that starts at 0x00778D24, or JMP SHORT Wow.00778D28. That is where we will start gathering information.
Line 26 has two memory reads on it. In simplistic terms, it looks more or less like so: [[0x00778D24 + 0x16]] = g_clientConnection. It reads from tlscode+0x16 (0x00778D3A), which contains 0x00D495B0, and then reads from 0xD495B0 to get the address of g_clientConnection. My hope is that the pointer to g_clientConnection will always be 0x16 bytes from where my pattern is found and, therefore, will update itself with each new client.
Line 30 has two more memory reads on it. [g_clientConnection + [tlscode+0x22]] = s_curMgr. Basically, I'm not taking chances that the offset from g_clientConnection to the s_curMgr pointer will always stay the same, so I read the offset from memory at tlscode+0x22 (0x00778D46). Hopefully this will solve some client update issues, as well, though it's less likely. Never hurts to be safe, though.
Seriously, I have no idea if any of this helps in any way, as I've never had things break during a client update (seeing as I'm starting from scratch with today as my first day). All I'm trying to do is fix problems I've had happen to me in other games when clients update and hope that WoW will act the same way. I'm sure someone will correct me if I'm wrong--in fact, I'm hoping for it .
Here's a post I made on a website for a different game that may explain dwFindPattern usage better, for those of you who aren't already acquainted with it: [Only registered and activated users can see links. ]. You shouldn't have to register to view it.