I'm still trying to understand what exactly the client is doing during startup. I wrote the following program to rebuild the behaviour of the client. It's not really good yet, especially the part that is mimicing the winsock stuff. I'm certainly unclear about what the client is sending. First I thought it was constant data, but then again it was completely different. Anyway, here is the program:
/* winegcc lotro-behaviour.c -Isource/wine/include/ -lws2_32 -o lotro-behaviour && ./lotro-behaviour.exe */
#include
#include
#include
/* Copy from rsaenh.c */
#define RSAENH_MAGIC_RSA2 0x32415352
#define RSAENH_MAGIC_RSA1 0x31415352
/* This method is used to dump a region of memory */
static void byte_dump(CONST BYTE * pbData, DWORD dwDataLen) {
DWORD i;
if (pbData && dwDataLen > 0) {
for (i = 0; i < dwDataLen; i++) {
printf("0x%02x (%d)\n", pbData[i], pbData[i]);
}
}
}
/* Load the clients private key into the given pointer */
static void load_private_key(CONST BYTE * pbData) {
BLOBHEADER * pHeader = (BLOBHEADER *) pbData;
pHeader->bType = PRIVATEKEYBLOB;
pHeader->bVersion = 0x2;
pHeader->reserved = 0;
pHeader->aiKeyAlg = CALG_RSA_KEYX;
RSAPUBKEY * pRsaPubKey = (RSAPUBKEY *) (pbData + sizeof(BLOBHEADER));
pRsaPubKey->magic = RSAENH_MAGIC_RSA2;
pRsaPubKey->bitlen = 512;
pRsaPubKey->pubexp = 1;
BYTE pbSrc[288] = {
0xab, 0xef, 0xfa, 0xc6, 0x7d, 0xe8, 0xde, 0xfb,
0x68, 0x38, 0x09, 0x92, 0xd9, 0x42, 0x7e, 0x6b,
0x89, 0x9e, 0x21, 0xd7, 0x52, 0x1c, 0x99, 0x3c,
0x17, 0x48, 0x4e, 0x3a, 0x44, 0x02, 0xf2, 0xfa,
0x74, 0x57, 0xda, 0xe4, 0xd3, 0xc0, 0x35, 0x67,
0xfa, 0x6e, 0xdf, 0x78, 0x4c, 0x75, 0x35, 0x1c,
0xa0, 0x74, 0x49, 0xe3, 0x20, 0x13, 0x71, 0x35,
0x65, 0xdf, 0x12, 0x20, 0xf5, 0xf5, 0xf5, 0xc1,
0xed, 0x5c, 0x91, 0x36, 0x75, 0xb0, 0xa9, 0x9c,
0x04, 0xdb, 0x0c, 0x8c, 0xbf, 0x99, 0x75, 0x13,
0x7e, 0x87, 0x80, 0x4b, 0x71, 0x94, 0xb8, 0x00,
0xa0, 0x7d, 0xb7, 0x53, 0xdd, 0x20, 0x63, 0xee,
0xf7, 0x83, 0x41, 0xfe, 0x16, 0xa7, 0x6e, 0xdf,
0x21, 0x7d, 0x76, 0xc0, 0x85, 0xd5, 0x65, 0x7f,
0x00, 0x23, 0x57, 0x45, 0x52, 0x02, 0x9d, 0xea,
0x69, 0xac, 0x1f, 0xfd, 0x3f, 0x8c, 0x4a, 0xd0,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64, 0xd5, 0xaa, 0xb1, 0xa6, 0x03, 0x18, 0x92,
0x03, 0xaa, 0x31, 0x2e, 0x48, 0x4b, 0x65, 0x20,
0x99, 0xcd, 0xc6, 0x0c, 0x15, 0x0c, 0xbf, 0x3e,
0xff, 0x78, 0x95, 0x67, 0xb1, 0x74, 0x5b, 0x60,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
memcpy((void *)pbData + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), &pbSrc, 288);
}
/* Load the servers public key into the given pointer */
static void load_public_key(CONST BYTE * pbData) {
BLOBHEADER * pHeader = (BLOBHEADER *) pbData;
pHeader->bType = PUBLICKEYBLOB;
pHeader->bVersion = 0x2;
pHeader->reserved = 0;
pHeader->aiKeyAlg = CALG_RSA_KEYX;
RSAPUBKEY * pRsaPubKey = (RSAPUBKEY *) (pbData + sizeof(BLOBHEADER));
pRsaPubKey->magic = RSAENH_MAGIC_RSA1;
pRsaPubKey->bitlen = 1024;
pRsaPubKey->pubexp = 0x010001;
BYTE pbSrc[128] = {
0x4d, 0x03, 0x8d, 0xd7, 0xd8, 0xa8, 0x85, 0x97,
0x2d, 0x04, 0xde, 0x57, 0xe2, 0x77, 0xc4, 0xbf,
0xc6, 0x13, 0x15, 0x0e, 0x26, 0x72, 0x99, 0x11,
0xa1, 0xcd, 0xf3, 0x8f, 0xd5, 0x5d, 0x63, 0x4c,
0xf3, 0x75, 0xb7, 0x62, 0x9d, 0xee, 0xe8, 0xd5,
0xc5, 0xa7, 0x0e, 0x3c, 0x9c, 0x51, 0xd4, 0x3f,
0x00, 0x45, 0xb2, 0xed, 0x19, 0xb1, 0xca, 0xda,
0x84, 0x1c, 0x6b, 0x35, 0x41, 0x17, 0x46, 0xd0,
0x56, 0xb9, 0x4c, 0x06, 0xcd, 0x8d, 0xc5, 0x50,
0x39, 0x36, 0x4e, 0xe3, 0x9b, 0x5e, 0x3c, 0x44,
0xe8, 0x04, 0x0e, 0x90, 0xc4, 0x89, 0xfc, 0x24,
0xe9, 0xc4, 0xff, 0xc6, 0x97, 0xfd, 0xd6, 0x5d,
0x2b, 0x67, 0x5c, 0xa2, 0x7f, 0x8d, 0x2a, 0xb9,
0x5a, 0x52, 0x3c, 0xec, 0x0d, 0xc1, 0x55, 0xc8,
0xc1, 0xb0, 0x07, 0x3a, 0xdc, 0x03, 0x9c, 0x38,
0x34, 0x08, 0x0e, 0x19, 0x27, 0xcd, 0x7b, 0xd0
};
memcpy((void *)pbData + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), &pbSrc, 128);
}
void main() {
int retval;
char* localIP;
BOOLEAN first = TRUE;
BYTE bDataPrivKey[sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + 288];
BYTE bDataPubKey[sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + 128];
WSADATA wsaData;
retval = WSAStartup(2, &wsaData);
if (retval != 0) {
printf("WSAStartup returned %d\n", retval);
return;
}
SOCKET socket = WSASocket(AF_INET, SOCK_DGRAM, 0, (LPWSAPROTOCOL_INFO) NULL, 0, 0x1 /* What is this for */);
BYTE name[256];
gethostname(&name, 256);
struct hostent* localHost = gethostbyname((const CHAR *) &name);
localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
/* addr.sin_addr.s_addr = inet_addr(localIP); */
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
addr.sin_port = htons(10691);
retval = bind(socket, (const struct sockaddr *) &addr, sizeof(addr));
if (retval != 0) {
printf("bind returned %d\n", retval);
closesocket(socket);
WSACleanup();
return;
}
CHAR optval[4];
int optlen = 4;
retval = getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&optval, &optlen);
if (retval != 0) {
printf("bind returned %d\n", retval);
closesocket(socket);
WSACleanup();
return;
}
//byte_dump((CONST BYTE *) &optval, 4);
retval = getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&optval, &optlen);
//byte_dump((CONST BYTE *) &optval, 4);
HCRYPTPROV hProv;
CryptAcquireContext(&hProv, "", MS_ENHANCED_PROV, 1, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET);
/* Loads the private key */
BYTE bDataPriv[512];
DWORD dwDataPrivLen = 512;
first = TRUE;
do {
if (!CryptGetProvParam(hProv, PP_ENUMALGS_EX, (BYTE *)&bDataPriv, &dwDataPrivLen, first ? CRYPT_FIRST : CRYPT_NEXT)) {
printf("No more CryptProvParam to fullfill CALG_RSA_KEYX for Priv\n");
break;
}
first = FALSE;
} while (((PROV_ENUMALGS_EX *) &bDataPriv)->aiAlgid != CALG_RSA_KEYX);
load_private_key((CONST BYTE * ) &bDataPrivKey);
HCRYPTKEY hPrivKey;
CryptImportKey(hProv, (BYTE *)&bDataPrivKey, sizeof(bDataPrivKey), 0, CRYPT_EXPORTABLE, &hPrivKey);
/* Loads the public key */
BYTE bDataPub1[512];
DWORD dwDataPubLen1 = 512;
first = TRUE;
do {
if (!CryptGetProvParam(hProv, PP_ENUMALGS_EX, (BYTE *)&bDataPub1, &dwDataPubLen1, first ? CRYPT_FIRST : CRYPT_NEXT)) {
printf("No more CryptProvParam to fullfill CALG_RSA_KEYX for Pub\n");
break;
}
first = FALSE;
} while (((PROV_ENUMALGS_EX *) &bDataPub1)->aiAlgid != CALG_RSA_KEYX);
BYTE bDataPub2[512];
DWORD dwDataPubLen2 = 512;
first = TRUE;
do {
if (!CryptGetProvParam(hProv, PP_ENUMALGS_EX, (BYTE *)&bDataPub2, &dwDataPubLen2, first ? CRYPT_FIRST : CRYPT_NEXT)) {
printf("No more CryptProvParam to fullfill CALG_RC4 for Pub\n");
break;
}
first = FALSE;
} while (((PROV_ENUMALGS_EX *) &bDataPub2)->aiAlgid != CALG_RC4);
load_public_key((CONST BYTE * ) &bDataPubKey);
HCRYPTKEY hPubKey;
CryptImportKey(hProv, (BYTE *)&bDataPubKey, sizeof(bDataPubKey), 0, CRYPT_EXPORTABLE, &hPubKey);
/* CryptGenKey */
HCRYPTKEY hRcKey;
CryptGenKey (hProv, CALG_RC4, CRYPT_EXPORTABLE, &hRcKey);
/* CryptExportKey */
DWORD dwDataLen;
CryptExportKey(hRcKey, hPubKey, SIMPLEBLOB, 0, 0, &dwDataLen);
BYTE * pbData = malloc(dwDataLen);
CryptExportKey(hRcKey, hPubKey, SIMPLEBLOB, 0, pbData, &dwDataLen);
BYTE encryptData1[] = {
0x01, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
0x31
};
DWORD encryptData1Len = 9;
CryptEncrypt(hRcKey, 0, TRUE, 0, 0, &encryptData1Len, 9);
CryptEncrypt(hRcKey, 0, TRUE, 0, (BYTE *) &encryptData1, &encryptData1Len, 9);
char buffer0[] = {
0x54, 0xa7
};
char buffer1[] = {
0xb0, 0x02, 0x0f, 0x08, 0xed, 0x00, 0x00, 0x00,
0x48, 0xfe, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
char buffer2[] = {
0x48, 0xfe, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
};
WSABUF wsabufs[3];
wsabufs[0].len = sizeof(buffer0);
wsabufs[0].buf = malloc(sizeof(buffer0));
memcpy(wsabufs[0].buf, &buffer0, sizeof(buffer0));
wsabufs[1].len = sizeof(buffer1);
wsabufs[1].buf = malloc(sizeof(buffer1));
memcpy(wsabufs[1].buf, &buffer1, sizeof(buffer1));
wsabufs[2].len = sizeof(buffer2);
wsabufs[2].buf = malloc(sizeof(buffer2));
memcpy(wsabufs[2].buf, &buffer2, sizeof(buffer2));
struct sockaddr_in to;
to.sin_family = AF_INET;
to.sin_port = htons(9000);
to.sin_addr.s_addr = inet_addr("192.168.1.140");
DWORD bytesSent;
WSASendTo(socket, (LPWSABUF)&wsabufs, 3, &bytesSent, 0, (const struct sockaddr *) &to, 16, 0, 0);
free(pbData);
CryptReleaseContext(hProv, 0);
closesocket(socket);
WSACleanup();
}
Based on what I learned I tried to look at what the server needs to do as initial setup. I came up with the following dummy:
/* Run with: winegcc lotro-server.c -Isource/wine/include/ -lws2_32 -o lotro-server && ./lotro-server.exe */
#include
#include
#include
#include
/* This method is used to dump a region of memory */
static void byte_dump(CONST BYTE * pbData, DWORD dwDataLen) {
DWORD i;
if (pbData && dwDataLen > 0) {
for (i = 0; i < dwDataLen; i++) {
printf("0x%02x (%d)\n", pbData[i], pbData[i]);
}
}
}
int main() {
WORD wVersionRequested;
WSADATA wsaData;
int err = 0, remoteAddrLen = sizeof(SOCKADDR_IN);
SOCKET s = INVALID_SOCKET;
struct addrinfo hints, *result = NULL, *remoteAddr;
char buf[256];
/* Initialize winsock with version 2.2 */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != NO_ERROR) {
printf("WSAStartup failed: %d\n", err);
return -1;
}
/* Verify the system is winsock 2.2 capable */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("WSAStartup returned wrong version\n");
WSACleanup();
return -2;
}
/* Determine our local address */
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_PASSIVE;
err = getaddrinfo(NULL, "9000", &hints, &result);
if (err != 0) {
printf("getaddrinfo failed: %d\n", err);
WSACleanup();
return -3;
}
s = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (s == INVALID_SOCKET) {
printf("Creating the socket failed: %d\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return -4;
}
err = bind(s, result->ai_addr, (int)result->ai_addrlen);
if (err == SOCKET_ERROR) {
printf("Binding failed: %d\n", WSAGetLastError());
closesocket(s);
freeaddrinfo(result);
WSACleanup();
return -5;
}
printf("Waiting for client\n");
while(1) {
err = recvfrom(s, buf, 256, 0, (SOCKADDR *) &remoteAddr, &remoteAddrLen);
if (err == SOCKET_ERROR) {
printf("Recieve failed: %d\n", WSAGetLastError());
closesocket(s);
freeaddrinfo(result);
WSACleanup();
return -6;
}
printf("Read %d bytes\n", err);
byte_dump((CONST BYTE *) &buf, err);
err = sendto(s ,buf, err, 0, (SOCKADDR*)&remoteAddr, remoteAddrLen);
if (err == SOCKET_ERROR) {
printf("Send failed: %d\n", WSAGetLastError());
closesocket(s);
freeaddrinfo(result);
WSACleanup();
return -7;
}
printf("Sent %d bytes\n", err);
}
closesocket(s);
freeaddrinfo(result);
WSACleanup();
return 0;
}