 |
-
Contributor
Join Date: May 2008
Posts: 388
Rep: 90
Cash: 605
Proof of Concept: dwFindPattern TLS [C#]
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).
(Source with highlighting HERE.)
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 here. 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 01:02 PM.
Reason: added credits
-
Contributor
Join Date: May 2008
Posts: 388
Rep: 90
Cash: 605
Further explanation...
Okay, on line 21:
Code:
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");
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:
Code:
00778D1B |. 6A 00 PUSH 0 ; /Arg1 = 00000000
00778D1D |. 8BC8 MOV ECX,EAX ; |
00778D1F |. E8 ECE8FFFF CALL <Wow.CGCurMgr_C> ; Wow.00777610
00778D24 |. EB 02 JMP SHORT Wow.00778D28
00778D26 33C0 XOR EAX,EAX
00778D28 64:8B15 2C000000 MOV EDX,DWORD PTR FS:[2C]
00778D2F |. 8B0D 84AAE800 MOV ECX,DWORD PTR DS:[E8AA84]
00778D35 |. 8B0C8A MOV ECX,DWORD PTR DS:[EDX+ECX*4]
00778D38 |. 8B15 B095D400 MOV EDX,DWORD PTR DS:[D495B0]
00778D3E |. 8981 10000000 MOV DWORD PTR DS:[ECX+10],EAX
00778D44 |. 8982 18220000 MOV DWORD PTR DS:[EDX+2218],EAX
00778D4A |. A1 B095D400 MOV EAX,DWORD PTR DS:[D495B0]
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 .
Afterthought: an image says a thousand words?
-
Warden's Mediator
Join Date: Dec 2006
Location: Raping your Stack
Posts: 891
Rep: 822
Cash: 610
-
Contributor
Join Date: May 2008
Posts: 388
Rep: 90
Cash: 605
Nothing you would change?
-
Master Sergeant
Join Date: May 2008
Posts: 96
Rep: 7
Cash: 600
another location is here:
0077624E CC INT3
0077624F CC INT3
00776250 55 PUSH EBP
00776251 8BEC MOV EBP,ESP
00776253 A1 84AAE800 MOV EAX,DWORD PTR DS:[E8AA84]
00776258 64:8B0D 2C000000 MOV ECX,DWORD PTR FS:[2C]
0077625F 53 PUSH EBX
00776260 56 PUSH ESI
00776261 8B3481 MOV ESI,DWORD PTR DS:[ECX+EAX*4]
00776264 8B86 10000000 MOV EAX,DWORD PTR DS:[ESI+10]
0077626A 05 A8000000 ADD EAX,0A8
0077626F 8B40 04 MOV EAX,DWORD PTR DS:[EAX+4]
00776272 A8 01 TEST AL,1
00776274 57 PUSH EDI
with the:
00776253 A1 84AAE800 MOV EAX,DWORD PTR DS:[E8AA84]
00776258 64:8B0D 2C000000 MOV ECX,DWORD PTR FS:[2C]
being the most important location.
theres a lot of stable (non-changing) code around that spot as well.
best,
Cal
-
Contributor
Join Date: May 2008
Posts: 388
Rep: 90
Cash: 605
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: dwFindPattern Proof of Concept [C#] - Dark Ages Underground. You shouldn't have to register to view it.
-
New User
Join Date: Mar 2009
Posts: 14
Rep: -3
Cash: 600
Please, can someone compile this code to .exe?
I downloaded many proggrams, but can't launch code..
I'm trying to find pointers to ObjectManager in 1.12.1 patch, for a week, but can't figure this out...please any help..
Last edited by DEMON_PK; 05-27-2010 at 08:36 AM.
-
Master Sergeant
Join Date: Apr 2008
Location: Denmark
Posts: 116
Rep: 22
Cash: 600
Please, can someone compile this code to .exe?
I downloaded many proggrams, but can't launch code..
I'm trying to find pointers to ObjectManager in 1.12.1 patch, for a week, but can't figure this out...please any help..
notepad.exe -> new file -> paste code -> save as Program.exe -> run it
-
New User
Join Date: Mar 2009
Posts: 14
Rep: -3
Cash: 600
 Originally Posted by XTZGZoReX
notepad.exe -> new file -> paste code -> save as Program.exe -> run it
Thank you, but this don't work..
-
MMOwned WebDev
Join Date: Jan 2008
Posts: 2,555
Rep: 1167
Cash: 350
-
Contributor
Join Date: Jan 2008
Location: Sweden
Posts: 718
Rep: 173
Cash: 580
 Originally Posted by DEMON_PK
Thank you, but this don't work..
He forgot the vital part, you must append
Code:
#Compile -r -i -f -k
to the program.
Good luck.
Welcome to this section, please read the rules *cough*
Dahnniel [DOT] s [AT] gmail [DOT] com

-
Corporal
Join Date: Nov 2008
Posts: 17
Rep: 5
Cash: 600
 Originally Posted by DEMON_PK
Thank you, but this don't work..
You have to convert the code first. To do so you have to reverse the complete text.
For example:
Code:
return dwFindPattern(hProcess, start, length, _pattern, mask, ' ');
ist going to be
Code:
;)' ' ,ksam ,nrettap_ ,htgnel ,trats ,ssecorPh(nrettaPdniFwd nruter
This is due the fact that the stack is growing downward.
Have fun!
-
Knight-Lieutenant
Join Date: Jan 2008
Location: South Pole
Posts: 381
Rep: 60
Cash: 600
bumping in a necro thread?
-
Site Donator
Join Date: Jan 2008
Posts: 370
Rep: 34
Cash: 240
 Originally Posted by fish2k
You have to convert the code first. To do so you have to reverse the complete text.
For example:
Code:
return dwFindPattern(hProcess, start, length, _pattern, mask, ' ');
ist going to be
Code:
;)' ' ,ksam ,nrettap_ ,htgnel ,trats ,ssecorPh(nrettaPdniFwd nruter
This is due the fact that the stack is growing downward.
Have fun!
you fool thats how they do it on linux!
On windows they use linked lists that means you need to put the -> sign between all words
Code:
->return-> ->dwFindPattern(->hProcess,-> start,-> length,-> _pattern,-> mask, ' ');->
-
New User
Join Date: Mar 2009
Posts: 14
Rep: -3
Cash: 600
sorry, but i need this 2 old offsets, and i will find it))
Founded them using CE and hand made memory scanner...
Thanks
Last edited by DEMON_PK; 06-10-2010 at 01:56 AM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
Forum Rules
|
 |
Bookmarks