I've been looking at WoW's packets that it sends when I move. I've been comparing the data to the opcodes in the MangOS source code. I haven't seen any of the opcodes pointed to by the parameter. Any help here?
I've been looking at WoW's packets that it sends when I move. I've been comparing the data to the opcodes in the MangOS source code. I haven't seen any of the opcodes pointed to by the parameter. Any help here?
What method are you using to monitor packets?
Not really much right now, I was just putting a bp in my detoured SendPacket function.
EDIT: Ok, in the SendPacket() function, what is the data stored as? I know that kynox uses a certain class for his packet, CDataStore or something like that... I'm gonna start logging the packets and see if I can see anything different.
Last edited by lanman92; 12-14-2008 at 04:19 PM.
Headers are encrypted afaik, you sure you're not looking at the encrypted header for the opcode?
If you hook the SendPacket and ProcessPacket functions you can avoid the encryption and dump out all the information you want (or modify it at will).
The address I'm detouring is 0x005843A0. I'm not sure if this is encrypted or not. In the case that it's not, where is the opcode located? I'd imagine it being right at the top...
https://svn.sourceforge.net/svnroot/...game/Opcodes.h
not sure where it's located but it shoud be in the start segments i guess
I have the whole thing downloaded on my PC :P I'm about to give this another go with logging and hoping I find it eventually. For all I know right now, I could be looking at pretty little ENCRYPTED packets...
EDIT: What logging lib do you guys use for VC++? I've googled but all I came up with is log4cxx, which appears to be for gcc.
Last edited by lanman92; 12-15-2008 at 10:43 AM.
Mangos/Ascent/Wcell/etc all detail the structures of packets (obviously they have to). I suggest checking them out.
Logging lib? I just log info to a text file using the STL... I don't know why you need to download a 3rd party library just to log crap. Fstream ftw.
The packet headers are indeed scrambled. They're xor'd with a key obtained by the SRP authentication. The key is 20 bytes long (Not sure where you'd find it in the client). Sent packets headers (Client->Server, AKA CMSG in emulators) are 6 bytes long, with a uint16 size, and uint32 opcode. Received messages (Server->Client, aka SMSG) are only 2 bytes, with just the opcode.
Here's a C# sniplet of my class to handle this encryption.
and the C++ versionCode:/// <summary> /// This class handles the encryption that is done on the headers of world server packets. /// The Key is 20 bytes long /// </summary> public class WoWCrypt { public class PacketKeyGenerator { static readonly byte[] SeedKey = { 0x38, 0xA7, 0x83, 0x15, 0xF8, 0x92, 0x25, 0x30, 0x71, 0x98, 0x67, 0xB1, 0x8C, 0x4, 0xE2, 0xAA }; public static byte[] GenerateKey(byte[] sessionKey) { byte[] firstBuffer = new byte[64]; byte[] secondBuffer = new byte[64]; memset(firstBuffer, 0x36); memset(secondBuffer, 0x5C); for (int i = 0; i < SeedKey.Length; i++) { firstBuffer[i] = (byte)(SeedKey[i] ^ firstBuffer[i]); secondBuffer[i] = (byte)(SeedKey[i] ^ secondBuffer[i]); } Sha1Hash sha = new Sha1Hash(); sha.Update(firstBuffer); sha.Update(sessionKey); byte[] tempDigest = sha.Final(); sha = new Sha1Hash(); sha.Update(secondBuffer); sha.Update(tempDigest); byte[] finalKey = sha.Final(); return finalKey; } private static void memset(byte[] buffer, byte value) { for (int i = 0; i < buffer.Length; i++) { buffer[i] = value; } } } private bool mInitialised = false; // Encryption state private byte mEncPrev; public int mEncIndex; // Decryption state public byte mDecPrev; public int mDecIndex; public byte[] mKey; public void Init(byte[] Key) { mKey = PacketKeyGenerator.GenerateKey(Key); mInitialised = true; } public byte GetDecPrev() { return mDecPrev; } public void SetDecPrev(byte SetTo) { mDecPrev = SetTo; } public int GetDecIndex() { return mDecIndex; } public void SetDecIndex(int SetTo) { mDecIndex = SetTo; } public void Decrypt(byte[] Data, int Length) { if (mInitialised == false) return; for(int i = 0; i < Length; ++i) { byte x = (byte)((Data[i] - mDecPrev) ^ mKey[mDecIndex]); ++mDecIndex; mDecIndex %= mKey.Length; mDecPrev = Data[i]; Data[i] = x; } } public void Encrypt(byte[] Data, int Length) { if (mInitialised == false) return; for(int i = 0; i < Length; ++i) { byte x = (byte)((Data[i] ^ mKey[mEncIndex]) + mEncPrev); ++mEncIndex; mEncIndex %= mKey.Length; mEncPrev = x; Data[i] = x; } } }
[edit]Code:class WowCrypt { public: WowCrypt(const unsigned int send, const unsigned int recv); ~WowCrypt(); const unsigned int CRYPTED_SEND_LEN; const unsigned int CRYPTED_RECV_LEN; void Init(); void SetKey(uint8 *, size_t); void GenKey(uint8 *, size_t, uint8 *); void DecryptRecv(uint8 *, size_t); void EncryptSend(uint8 *, size_t); bool IsInitialized() { return _initialized; } private: std::vector<uint8> _key; uint8 _send_i, _send_j, _recv_i, _recv_j; bool _initialized; }; #include <algorithm> WowCrypt::WowCrypt(const unsigned int send, const unsigned int recv) : CRYPTED_SEND_LEN(send), CRYPTED_RECV_LEN(recv) { _initialized = false; } void WowCrypt::Init() { _send_i = _send_j = _recv_i = _recv_j = 0; _initialized = true; } void WowCrypt::DecryptRecv(uint8 *data, size_t len) { if (!_initialized) return; if (len < CRYPTED_RECV_LEN) return; for (size_t t = 0; t < CRYPTED_RECV_LEN; t++) { _recv_i %= _key.size(); uint8 x = (data[t] - _recv_j) ^ _key[_recv_i]; ++_recv_i; _recv_j = data[t]; data[t] = x; } } void WowCrypt::EncryptSend(uint8 *data, size_t len) { if (!_initialized) return; if (len < CRYPTED_SEND_LEN) return; for (size_t t = 0; t < CRYPTED_SEND_LEN; t++) { _send_i %= _key.size(); uint8 x = (data[t] ^ _key[_send_i]) + _send_j; ++_send_i; data[t] = _send_j = x; } } void WowCrypt::SetKey(uint8 *key, size_t len) { _key.resize(len); std::copy(key, key + len, _key.begin()); } WowCrypt::~WowCrypt() { }
I've been thinking of doing a brute-forcer to brute force the key (fairly simple to do) on pcap's of wow's tcp stream. but that is another method of obtaining the encryption key without touching the client memory (but for you guys, whats the fun in that?). For example, capturing 20 or so ping packets (because they're sequential and easy to identify) would be sufficient data to brute the key. much like how WEP bruter's work.
Last edited by BoogieManTM; 12-15-2008 at 02:02 PM.
Ok, so I'm awfully confused. I'm detouring at 0x005843A0, is that even the correct address? I have it logging the value of the parameter and address everytime that it gets called, but it only logs once each time I log into WoW and inject... Here's my code.
Code:void __declspec(naked) __stdcall SendPacket(DWORD *pDataStore) { __asm pushad; addLog(pDataStore); __asm popad; __asm jmp orig_SendPacket; __asm retn; } void __stdcall addLog(DWORD *pDataStore) { fstream fs; if(!opened) { fs.open("C:\\Users\\Nick\\Documents\\wowlog.txt", ios::out | ios::app | ios::in); opened = true; } DWORD data = *pDataStore; fs << data << endl; fs << pDataStore << endl; return; }
Last edited by lanman92; 12-15-2008 at 04:46 PM.
yo yo, same shit diff day. I see you're having fun with the newbs 'round here, lol! some pretty entertaining reads.
I wouldn't be surprised if it was in CNetClient, it would be a fitting place for it.. but as I do all the key generation myself, I never had to fiddle around with how the client deals with it much. Either way, grabbing it from memory, or just brute forcing the damn thing (a few proxies have implemented this method before), its fairly straight forward get around the header encryption.
Tis stored in CNetClient indeed. But if this guy can't even detour a function, i hardly see this helping him.I wouldn't be surprised if it was in CNetClient, it would be a fitting place for it.. but as I do all the key generation myself, I never had to fiddle around with how the client deals with it much. Either way, grabbing it from memory, or just brute forcing the damn thing (a few proxies have implemented this method before), its fairly straight forward get around the header encryption.
PS: HAI BOOGIE
Just wanted to add a bit more info
BoogieMan, you can replace the C# PacketGenerator class with 1 line:
byte[] mKey = new System.Security.Cryptography.HMACSHA1(EncryptionSeed).ComputeHash(SessionKey);
The key generation is just HMAC sha1 seeded with those 16 static bytes.
Also, packets can have either 2 or 3 bytes in the header for the size field, depending on how big the packet is. If the packet is over 32767 bytes, 3 bytes are used, and the first byte is OR'd with 0x80 as a marker
if (size > 32767)
{
packetHeader[0] = ((size >> 16) & 0xFF) | 0x80;
packetHeader[1] = (size >>& 0xFF;
packetHeader[2] = size & 0xFF;
}
else
{
packetHeader[0] = (size >>& 0xFF;
packetHeader[1] = size & 0xFF;
}
This goes for both server-sent and client-sent packets
Now for some offsets
At (ClientConnection + 0x27E4) is a pointer to the class that handles the actual communication with the server. I just called this WoWConnection, but it could be the CNetClient
struct WoWConnection
{
int field_0;
SOCKET Socket;
int field_8;
int field_C;
int field_10;
ClientConnection* ClientConnectionPtr;
int field_18;
char *InputBuffer;
int InputBufferPosition;
int InputBufferSize;
int field_28[27];
struct _RTL_CRITICAL_SECTION CS_field_94;
int field_AC;
int ProcessingThreadId;
ClientConnection* SavedClientConnectionContext;
int field_B8[9];
WDSNode PacketQueue;// 0xDC - 0xE8
struct _RTL_CRITICAL_SECTION CS_field_E8;
int field_100[6];
char IsEncryptionInitialized;// 0x118
char EncKeyIndex;
char EncKeyPrevious;
char ClientOpcodeLen; // always 4
char DecKeyIndex;
char DecKeyPrevious;
char ServerOpcodeLen; // always 2
char EncryptionKey[20]; // 0x11F - 0x133 this is whats used to encrypt packets
}
Now if you need the full 40byte sessionkey for some reason, its at ClientConnection+0x288.
The client still keeps this because its used to seed warden modules and hashed in one of the bot packets, but thats another matter
And if you want to see the CDataStore class for the packets, go to https://starfish.westmont.edu/viewcv...tarblabIT/src/ and check out datastore.cpp/h. Its looks to be the exact same as what the client uses, based on some of the function names in the asserts of the ptr clients
-Ralek
Still waiting on my new account to activate, been a few days now. Had to grab this old one from way back
About time we had someone with sense post in these forums. Welcome!