Register for your free account! | Forgot your password?

You last visited: Today at 14:10

  • Please register to post and access all features, it's quick, easy and FREE!

 

Gameserver

Reply
 
Old   #1
 
elite*gold: 0
Join Date: Jun 2013
Posts: 16
Received Thanks: 2
Gameserver

posted this on another forum a long time ago but it didnt gain any traction so maybe people here will have use

Quote:
I decided to meddle around into guild wars 2 and found that their client/server architecture is roughly the same as the first game so, made for easy reverse engineering work

Code:
 template <typename T>
class gw_array
{
protected:
	T* arr_;
	DWORD allocatedsize_;
	DWORD currentsize_;
	DWORD unk_;
public:
	inline T& operator[](DWORD index) { return arr_[index]; }
	inline const DWORD& size() const { return currentsize_; }
};

using CtoSSend_t = void(__fastcall*)(void* thisptr, DWORD size, BYTE* packet);

template <typename packet>
using StoCHandler = bool(__fastcall*)(DWORD unk,packet* pak);

struct StoCPacketbase
{
	WORD header;
};

struct CtoSTableIndex 
{
	DWORD paramcount;
	DWORD* params;
};

template <typename packet>
struct StoCTableIndex
{
	DWORD paramcount;
	DWORD* params;
	DWORD unk;
	StoCHandler<packet> handler;
};


class GameServer {
	GameServer(){}
	GameServer(GameServer&){}
public:

	BYTE pad1[0xC];
	struct {
		BYTE pad1[0x2C];
		gw_array<CtoSTableIndex> ctos_table;
		DWORD unk1;
		gw_array<StoCTableIndex<StoCPacketbase>> stoc_table;
	} *consts;

};

GWCA::Hook ctos_log;
CtoSSend_t ctos_trampoline;
ConsoleEx* con = nullptr;

void __fastcall ctos_detour(GameServer* thisptr, DWORD size, CtoGS::Packet* packet)
{
	CtoGS::P010_UpdatePlayerPosition* spec;
	switch (packet->header){
	default:
		con->printf("<CtoS> Sz: %X Header: %X\n", size, *(WORD*)packet);
		for (DWORD i = 0; i < size; ++i){
			if (i % 16 == 0) con->printf("\n");
			con->printf("%02X ", ((BYTE*)packet)[i]);
		}
		con->printf("\n --------------------- \n");
		break;
	}
	return ctos_trampoline(thisptr, size, (BYTE*)packet);
}

void WINAPI init_consoleex(HMODULE module)
{
	con = ConsoleEx::GenerateLogger(module, "[CtoGS] Packet Log", CONSOLEEX_PARAMETERS(80, 20, 500, true));
	GWCA::PatternScanner scan;
	BYTE* ctos_hook = (BYTE*)scan.FindPattern("\x55\x8B\xEC\x83\xEC\x2C\x53\x56\x57\x8B\xF1\x8B\xFA", "xxxxxxxxxxxxx", 0);
	if (ctos_hook){
		con->printf("ctos = %X\n", ctos_hook);
		ctos_trampoline = (CtoSSend_t)ctos_log.Detour(ctos_hook, (BYTE*)ctos_detour, 6);
	}
	else{
		con->printf("ctos = ERR");
	}

	while (con->isOpen()) Sleep(100);

	ctos_log.Retour();
	delete con;
	FreeLibraryAndExitThread(module, EXIT_SUCCESS);
}

BOOL WINAPI DllMain(HMODULE hDllHandle, DWORD dwReason, LPVOID lpreserved)
{
	if (dwReason == DLL_PROCESS_ATTACH){
		DisableThreadLibraryCalls(hDllHandle);
		CreateThread(0, 0, (LPTHREAD_START_ROUTINE)init_consoleex, hDllHandle, 0, 0);
	}
	return TRUE;
}

The function hooked is the outgoing packet sender for the gameserver, this is before encryption so you can see the juicy details. It is an object method so ECX is the thisptr. In this object using what is provided, you can find a table holding handlers to each incoming packet handler. Along with encodings for parameters to expect in the packet. The encodings also exist for the ctos packets in a table above.

You can hook the stoc handlers using a simple vmt-like swap if desired.

GWCA::PatternScanner is a simple pattern scanner with wildcard support, nothing fancy, can use your own. (Make sure you start the scan from the module base, it changes)

GWCA::Hook is just a wrapper for a JMP hook w/ trampoline, so again, just use your own.

ConsoleEx can be found in the hacklib project .

Have fun, would love to see some documentation on the ctos packets .

Also note, there are references to the thisptr in static mem, so you can retrieve it and emulate calls to the sender for hacks/bots if one were so inclined.



4D1 is offline  
Thanks
2 Users
Old   #2
 
elite*gold: 0
Join Date: Nov 2007
Posts: 1,237
Received Thanks: 203
too high for me ^^


LasseK is offline  
Old   #3
 
elite*gold: 0
Join Date: Sep 2009
Posts: 4
Received Thanks: 5
GW2 has such hard packets
if packet contains string then function ctos_detour receive in CtoGS::Packet* packet something like that <prefix><some unknown bytes><pointer to string in memory>
the same situation with CS skill packets(coordinates), SC NPC shop(prices), CS\SC chat(strings)
int_ is offline  
Old   #4
 
elite*gold: 0
Join Date: Dec 2015
Posts: 76
Received Thanks: 23
Quote:
Originally Posted by int_ View Post
GW2 has such hard packets
if packet contains string then function ctos_detour receive in CtoGS::Packet* packet something like that <prefix><some unknown bytes><pointer to string in memory>
the same situation with CS skill packets(coordinates), SC NPC shop(prices), CS\SC chat(strings)
You are capturing unpacked version of the message before it enters the message queue. If you want to capture the packed version hook the sender function before call to RC4Crypt. Pointer to the packet should be in the EBX register.


Agedyn is offline  
Thanks
1 User
Old   #5
 
elite*gold: 0
Join Date: Sep 2009
Posts: 4
Received Thanks: 5
Agedyn, thanks
RC4 is called for composite packets, which may contain several messages. To analyze these packages difficult, because you must correctly parse each component of its package.
I found a place where you can take outgoing packets prepared for sending

Unfortunately, I do not know what to do with incoming packets, because they are compressed. You do not know what kind of compression algorithm used for incoming packets? I found the function of decompressing the data, but it is recursive, and dynamically allocates memory for uncompressed data, me too complicated to understand it
int_ is offline  
Old   #6
 
elite*gold: 0
Join Date: Dec 2015
Posts: 76
Received Thanks: 23
Quote:
Originally Posted by int_ View Post
Agedyn, thanks
RC4 is called for composite packets, which may contain several messages. To analyze these packages difficult, because you must correctly parse each component of its package.
I found a place where you can take outgoing packets prepared for sending

Unfortunately, I do not know what to do with incoming packets, because they are compressed. You do not know what kind of compression algorithm used for incoming packets? I found the function of decompressing the data, but it is recursive, and dynamically allocates memory for uncompressed data, me too complicated to understand it
Received messages are encrypted using RC4. They use the same key from sending and receiving.
LZ4 or LZMA is probably used for decompression (0xF5AF70).

Edit: LZ4 is the algorithm used for decompression. I found this by checking THIRDPARTYSOFTWAREREADME.txt for used compression algorithms (I found LZ4, LZMA and zlib) and then googling their working principle. The compression algorithm seemed similar to LZ4, so I tried to decompress packet using it and it worked just fine. LZ4 header starts at 4th byte.
Edit: First short is compressed size and second is uncompressed size.

Some messages contain compressed long fields. The size of these fields can be anywhere between 1 to 4 byte. uncompressLong is located at 0xDBA710. 0xDB9E50 is the compressLong function. .

As I'm working on a 'fake' client using NodeJS I have converted these algorithms to JavaScript.

Code:
    static compressLong(value, unk) {
        let compressed = 0;
        if (unk) {
            value &= -((value & 0x40000000) != 0);
        }
        let i = 0;
        do
        {
            let compressedByte = value & 0x7F;
            value >>= 7;
            if (value) {
                compressedByte |= 0x80;
            }
            compressed |= compressedByte << (i * 8);
            i++;
        } while(value);

        return compressed;
    }

    static uncompressLong(value) {
        let uncompressed = 0;
        let i = 0;
        let outSize = 0;
        for (; i < 4; ++i) {
            let byte = (value >> i * 8) & 0xFF;
            byte = ~(byte >>= 7);
            ++outSize;

            if ((byte & 1) != 0) {
                break;
            }
        }

        for (; i >= 0; --i) {
            let byte = (value >> i * 8) & 0xFF;
            uncompressed = uncompressed << 7 | byte & 0x7F;
        }
        return {value: uncompressed, size: outSize};
    }
0xDBA0C0 is used for unpacking message into a struct and then it's passed to dispatch function (0xDB87AD).

Here are ids for message fields:

Code:
# snippet from my python message dumper (may not be 100 % correct)
net_fields_send = {
    'MP_END': 0,
    'MP_MSGID': 1,
    'MP_BYTE': 2,
    'MP_SHORT': 3,
    'MP_COMPRESSED_LONG': 4,
    'MP_ALIGNED_LONGLONG': 5,
    'MP_FLOAT': 6,
    'MP_VEC2': 7,
    'MP_VEC3': 8,
    'MP_VEC4': 9,
    'MP_16BYTES_2': 10,
    'MP_16BYTES': 11,
    'MP_28BYTES': 12,
    'MP_STRING': 13,
    'MP_CSTRING': 14,
    'MP_OPTIONAL': 15,
    'MP_ARRAY_FIXED': 16, # message specific size
    'MP_ARRAY_SMALL': 17,
    'MP_ARRAY_LARGE': 18,
    'MP_BUFFER_FIXED': 19, # message specific size
    'MP_BUFFER_SMALL': 20,
    'MP_BUFFER_LARGE': 21,
    'MP_SRV_ALIGN': 22,
    'MP_SRV_END': 24
}

# gw2 unpack function decreases field id by one. Does not have MP_END field.
net_fields_recv = {
    'MP_MSGID': 1,
    'MP_BYTE': 2,
    'MP_SHORT': 3,
    'MP_COMPRESSED_LONG': 4,
    'MP_ALIGNED_LONGLONG': 5,
    'MP_FLOAT': 6,
    'MP_VEC2': 7,
    'MP_VEC3': 8,
    'MP_VEC4': 9,
    'MP_16BYTES_2': 10,
    'MP_16BYTES': 11,
    'MP_28BYTES': 12,
    'MP_STRING': 13,
    'MP_CSTRING': 14,
    'MP_OPTIONAL': 15,
    'MP_ARRAY_FIXED': 16, # message specific size
    'MP_ARRAY_SMALL': 17,
    'MP_ARRAY_LARGE': 18,
    'MP_BUFFER_FIXED': 19, # message specific size
    'MP_BUFFER_SMALL': 20,
    'MP_BUFFER_LARGE': 21,
    'MP_SRV_ALIGN': 22,
    'MP_SRV_END': 24
}
All addresses are for 63845 32-bit build

Edit: Renamed one net field.
Agedyn is offline  
Old   #7
 
elite*gold: 0
Join Date: Dec 2015
Posts: 76
Received Thanks: 23
Updated compression to handle 4 byte values.

Code:
    static compressLong(value, unk) {
        let compressed = [];

        if (unk) {
            value &= -((value & 0x40000000) != 0);
        }
        let i = 0;
        do
        {
            let b = value & 0x7F; // get all bits except most significant
            value >>= 7;

            let bitsShifted = 7 * (i + 1);

            // check if maximum size exceeded
            if (bitsShifted > 35) {
                break;
            }

            if (bitsShifted == 35) {
                b &= 0x0F; // 4 most significant bits are overflow, so discard them
            } else if (value != 0) { // if not the last byte, set MSB to indicate continuation
                b |= 0x80;
            }
            compressed.push(b);
            i++;
        } while(value != 0);
        return compressed;
    }

    static uncompressLong(buffer) {
        let uncompressed = 0;
        let outSize = 0;

        for (let i = 0; i < buffer.length; ++i) {
            let b = buffer[i];
            uncompressed |= (b & 0x7F) << (i * 7);
            outSize++;

            // check if MSB indicates end
            if ((b & 0x80) == 0) {
                break;
            }
        }
        return {value: uncompressed >>> 0, size: outSize};
    }


Agedyn is offline  
Reply



« Previous Thread | Next Thread »

Similar Threads
[Selling] Silver Hosting | Gameserver | ★Dein High-End Gameserver zum günstigen Preis★
Sehr geehrte Mitglieder von Elitepvpers, hiermit möchten wir Ihnen High-End Gameserver zum günstigen Preis anbieten. Gameserver - Multi Theft...
25 Replies - elite*gold Trading
Forum.Zap-Hosting.com - kostenlose Gameserver - Das Gameserver-Communityboard
Forum.Zap-Hosting.com - Das Gameserver - Communityboard http://zap-hosting.com/images/zapcommunity.png Zum Board Kostenlose Voice- und...
11 Replies - Counter-Strike



All times are GMT +2. The time now is 14:10.


Powered by vBulletin®
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.

Support | Contact Us | FAQ | Advertising | Privacy Policy
Copyright ©2017 elitepvpers All Rights Reserved.