Samstag, 9. Oktober 2010

On to the character selection

As you might have seen in this this video we got to the point of entering the character selection screen. This is a really good start. Today I got the latest copy of code by the videos author. I'm currently reviewing it and will hopefully put it to gitorious later.

The code itself is written using C#. This allows rapid prototyping of applications and good development speed. The code is capable of handling the login but you can't play the game yet. It basically is split into 2 states
  • loginStage
  • marginStage
During the loginState 15 packets will be sent by the server. In the following marginState 4 packets will be sent. We don't handle ping-pong-packets to verify the server is up and running yet. Also right now the code is not multi threaded.

The biggest downside right now is that we can send the packets the client expects but don't know what they mean in detail.

So stay tuned for further updates.

Mittwoch, 7. April 2010

Moving the code to gitorious

I wanted to test gitorious for a while now so I decided it was a good idea to host the code I wrote there. This allows regular readers to follow the progress and even to contribute back.

Right now it's a big junkyard but I'll continue cleaning it up as soon as time permits.

A few words to the repositories:

  • gls is the meta server written in C# which provides the infratructure to select a server from the client
  • htdocs is the content the client needs to properly display something
  • server is where the server code will be written
  • patches is the set of wine patches I wrote to trace what lotro is doing

http://gitorious.org/lotro/patches

Sonntag, 17. Januar 2010

Mimicing the clients behaviour

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;
}

Dienstag, 12. Januar 2010

Houston, we have a key

I finally got some time to hack around in the source code of wine to allow it to dump the key that will be imported. This hacky patch is capable of dumping the private RSA key that will be used for exchanging the RC4 key that will be used during client and server communication.

diff --git a/dlls/rsaenh/rsaenh.c b/dlls/rsaenh/rsaenh.c
index 665bc40..9c9dd5e 100644
--- a/dlls/rsaenh/rsaenh.c
+++ b/dlls/rsaenh/rsaenh.c
@@ -3016,9 +3016,16 @@ static BOOL import_key(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen,
 BOOL WINAPI RSAENH_CPImportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen,
                                HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey)
 {
+    DWORD i;
     TRACE("(hProv=%08lx, pbData=%p, dwDataLen=%d, hPubKey=%08lx, dwFlags=%08x, phKey=%p)\n",
         hProv, pbData, dwDataLen, hPubKey, dwFlags, phKey);

+    if (pbData && dwDataLen > 0) {
+        for (i = 0; i < dwDataLen; i++) {
+            printf("0x%02x (%d)\n", pbData[i], pbData[i]);
+       }
+    }
+
     if (dwFlags & CRYPT_IPSEC_HMAC_KEY)
     {
         FIXME("unimplemented for CRYPT_IPSEC_HMAC_KEY\n");
This patch dumps the 308 bytes of data that are the exported private key. So you are wondering how the private key looks like?
0x07 (7)
0x02 (2)
0x00 (0)
0x00 (0)
0x00 (0)
0xa4 (164)
0x00 (0)
0x00 (0)
0x52 (82)
0x53 (83)
0x41 (65)
0x32 (50)
0x00 (0)
0x02 (2)
0x00 (0)
0x00 (0)
0x01 (1)
0x00 (0)
0x00 (0)
0x00 (0)
0xab (171)
0xef (239)
0xfa (250)
0xc6 (198)
0x7d (125)
0xe8 (232)
0xde (222)
0xfb (251)
0x68 (104)
0x38 (56)
0x09 (9)
0x92 (146)
0xd9 (217)
0x42 (66)
0x7e (126)
0x6b (107)
0x89 (137)
0x9e (158)
0x21 (33)
0xd7 (215)
0x52 (82)
0x1c (28)
0x99 (153)
0x3c (60)
0x17 (23)
0x48 (72)
0x4e (78)
0x3a (58)
0x44 (68)
0x02 (2)
0xf2 (242)
0xfa (250)
0x74 (116)
0x57 (87)
0xda (218)
0xe4 (228)
0xd3 (211)
0xc0 (192)
0x35 (53)
0x67 (103)
0xfa (250)
0x6e (110)
0xdf (223)
0x78 (120)
0x4c (76)
0x75 (117)
0x35 (53)
0x1c (28)
0xa0 (160)
0x74 (116)
0x49 (73)
0xe3 (227)
0x20 (32)
0x13 (19)
0x71 (113)
0x35 (53)
0x65 (101)
0xdf (223)
0x12 (18)
0x20 (32)
0xf5 (245)
0xf5 (245)
0xf5 (245)
0xc1 (193)
0xed (237)
0x5c (92)
0x91 (145)
0x36 (54)
0x75 (117)
0xb0 (176)
0xa9 (169)
0x9c (156)
0x04 (4)
0xdb (219)
0x0c (12)
0x8c (140)
0xbf (191)
0x99 (153)
0x75 (117)
0x13 (19)
0x7e (126)
0x87 (135)
0x80 (128)
0x4b (75)
0x71 (113)
0x94 (148)
0xb8 (184)
0x00 (0)
0xa0 (160)
0x7d (125)
0xb7 (183)
0x53 (83)
0xdd (221)
0x20 (32)
0x63 (99)
0xee (238)
0xf7 (247)
0x83 (131)
0x41 (65)
0xfe (254)
0x16 (22)
0xa7 (167)
0x6e (110)
0xdf (223)
0x21 (33)
0x7d (125)
0x76 (118)
0xc0 (192)
0x85 (133)
0xd5 (213)
0x65 (101)
0x7f (127)
0x00 (0)
0x23 (35)
0x57 (87)
0x45 (69)
0x52 (82)
0x02 (2)
0x9d (157)
0xea (234)
0x69 (105)
0xac (172)
0x1f (31)
0xfd (253)
0x3f (63)
0x8c (140)
0x4a (74)
0xd0 (208)
0x01 (1)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x01 (1)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x64 (100)
0xd5 (213)
0xaa (170)
0xb1 (177)
0xa6 (166)
0x03 (3)
0x18 (24)
0x92 (146)
0x03 (3)
0xaa (170)
0x31 (49)
0x2e (46)
0x48 (72)
0x4b (75)
0x65 (101)
0x20 (32)
0x99 (153)
0xcd (205)
0xc6 (198)
0x0c (12)
0x15 (21)
0x0c (12)
0xbf (191)
0x3e (62)
0xff (255)
0x78 (120)
0x95 (149)
0x67 (103)
0xb1 (177)
0x74 (116)
0x5b (91)
0x60 (96)
0x01 (1)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
0x00 (0)
The first 20 bytes are header data and the rest is the key. This is really good! Now we can use the private key to generate a public key from it. But we need to code to recreated this key first. I reengineerd the code and it looks like this:
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>

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() {

 BLOBHEADER header;
 header.bType = 0x7; /* PRIVATEKEYBLOB */
 header.bVersion = 2; /* Must be 2 */
 header.reserved = 0; /* Must be 0 */
 header.aiKeyAlg = 0xa400;
 byte_dump((CONST BYTE *) &header, sizeof(BLOBHEADER));

 RSAPUBKEY rsaPubKey;
 rsaPubKey.magic = 0x32415352; /* RSAENH_MAGIC_RSA2 */
 rsaPubKey.bitlen = 0x00000200; /* 512 bit long */
 rsaPubKey.pubexp = 0x00000001;
 byte_dump((CONST BYTE *) &rsaPubKey, sizeof(RSAPUBKEY));

 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
 };
 byte_dump((CONST BYTE *) &pbSrc, 288);
}
Now we can pass this to CPImportKey and calculate the public (which is always possible from the private key). But first I need to figure out how to do so using the wincrypt API. Once we've done that we can do the following things.
  1. Create a server component that is using the public key to decrypt the packages passed to it.
  2. Dump what is returned by CryptGenRandom
  3. Find out what CryptCreateHash does and especially why it is used here
  4. Dump what is encrypted by CryptEncrypt
  5. Hack wine to use a faked public key that is imported during the second CPImportKey (as there is no way to calculate the private key for this)
There are much more things that will need to be done, but again we solved another mystery. Oh, and here is the dump of the import of the public key:
0x06 (6)
0x02 (2)
0x00 (0)
0x00 (0)
0x00 (0)
0xa4 (164)
0x00 (0)
0x00 (0)
0x52 (82)
0x53 (83)
0x41 (65)
0x31 (49)
0x00 (0)
0x04 (4)
0x00 (0)
0x00 (0)
0x01 (1)
0x00 (0)
0x01 (1)
0x00 (0)
0x4d (77)
0x03 (3)
0x8d (141)
0xd7 (215)
0xd8 (216)
0xa8 (168)
0x85 (133)
0x97 (151)
0x2d (45)
0x04 (4)
0xde (222)
0x57 (87)
0xe2 (226)
0x77 (119)
0xc4 (196)
0xbf (191)
0xc6 (198)
0x13 (19)
0x15 (21)
0x0e (14)
0x26 (38)
0x72 (114)
0x99 (153)
0x11 (17)
0xa1 (161)
0xcd (205)
0xf3 (243)
0x8f (143)
0xd5 (213)
0x5d (93)
0x63 (99)
0x4c (76)
0xf3 (243)
0x75 (117)
0xb7 (183)
0x62 (98)
0x9d (157)
0xee (238)
0xe8 (232)
0xd5 (213)
0xc5 (197)
0xa7 (167)
0x0e (14)
0x3c (60)
0x9c (156)
0x51 (81)
0xd4 (212)
0x3f (63)
0x00 (0)
0x45 (69)
0xb2 (178)
0xed (237)
0x19 (25)
0xb1 (177)
0xca (202)
0xda (218)
0x84 (132)
0x1c (28)
0x6b (107)
0x35 (53)
0x41 (65)
0x17 (23)
0x46 (70)
0xd0 (208)
0x56 (86)
0xb9 (185)
0x4c (76)
0x06 (6)
0xcd (205)
0x8d (141)
0xc5 (197)
0x50 (80)
0x39 (57)
0x36 (54)
0x4e (78)
0xe3 (227)
0x9b (155)
0x5e (94)
0x3c (60)
0x44 (68)
0xe8 (232)
0x04 (4)
0x0e (14)
0x90 (144)
0xc4 (196)
0x89 (137)
0xfc (252)
0x24 (36)
0xe9 (233)
0xc4 (196)
0xff (255)
0xc6 (198)
0x97 (151)
0xfd (253)
0xd6 (214)
0x5d (93)
0x2b (43)
0x67 (103)
0x5c (92)
0xa2 (162)
0x7f (127)
0x8d (141)
0x2a (42)
0xb9 (185)
0x5a (90)
0x52 (82)
0x3c (60)
0xec (236)
0x0d (13)
0xc1 (193)
0x55 (85)
0xc8 (200)
0xc1 (193)
0xb0 (176)
0x07 (7)
0x3a (58)
0xdc (220)
0x03 (3)
0x9c (156)
0x38 (56)
0x34 (52)
0x08 (8)
0x0e (14)
0x19 (25)
0x27 (39)
0xcd (205)
0x7b (123)
0xd0 (208)
Oh, and here is the code to dump the private key of the client and the public key of the server.
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>

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]);
  }
 }
}

static void dump_private_key() {
 BLOBHEADER header;
 header.bType = 0x7; /* PRIVATEKEYBLOB */
 header.bVersion = 2; /* Must be 2 */
 header.reserved = 0; /* Must be 0 */
 header.aiKeyAlg = 0xa400;
 byte_dump((CONST BYTE *) &header, sizeof(BLOBHEADER));

 RSAPUBKEY rsaPubKey;
 rsaPubKey.magic = 0x32415352; /* RSAENH_MAGIC_RSA2 */
 rsaPubKey.bitlen = 0x00000200; /* 512 bit long */
 rsaPubKey.pubexp = 0x00000001;
 byte_dump((CONST BYTE *) &rsaPubKey, sizeof(RSAPUBKEY));

 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
 };
 byte_dump((CONST BYTE *) &pbSrc, 288);
}

static void dump_public_key() {
 BLOBHEADER header;
 header.bType = 0x6; /* PUBLICKEYBLOB */
 header.bVersion = 2; /* Must be 2 */
 header.reserved = 0; /* Must be 0 */
 header.aiKeyAlg = 0xa400;
 byte_dump((CONST BYTE *) &header, sizeof(BLOBHEADER));

 RSAPUBKEY rsaPubKey;
 rsaPubKey.magic = 0x32415352; /* RSAENH_MAGIC_RSA2 */
 rsaPubKey.bitlen = 0x00000400; /* 512 bit long */
 rsaPubKey.pubexp = 0x00010001;
 byte_dump((CONST BYTE *) &rsaPubKey, sizeof(RSAPUBKEY));

 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
 };
 byte_dump((CONST BYTE *) &pbSrc, 128);
}

int main() {
 dump_private_key();
 printf("\n");
 dump_public_key();
}
So it looks to me that:
  1. The client has it's own private and public RSA-keyset
  2. The server has a different RSA-one, too
  3. These keys are used to exchange the symetric RC4 key

It is really getting complicated.

Donnerstag, 17. Dezember 2009

How to start a LOTRO client

I'm currently looking into understanding the network protocol better. I use the following command to see what the client is doing:


lotroclient.exe
-a 2
-h 192.168.1.123:9000
--glsticketdirect 1
--chatserver 192.168.1.124:2900
--language fr
--bugurl http://192.168.1.125/bug.php
--supporturl http://192.168.1.126/TSSTrowser/Trowser.aspx
-GameType LOTROEU
--authserverurl https://lotroeugls.com/gls.authserver/service.asmx
--glsticketlifetime 86400


Note that this assumes you are running on a system with a hacked /etc/hosts file so you don't actually connect to lotroeugls.com!

I'll explain the options I used in detail to show you what I am doing:
-a 2
: I just a invalid username (I don't want to use a real one for privacy reasons). You can change this to whatever you like. This allows you to find the name in the network traffic.
-h 192.168.1.123
: This is bogus IP on the same local network as I am using. This is necessary to filter packets by IP to identify which host a client sends packets to
--glsticketdirect 1
: A bogus GLS ticket. Could be whatever you like. I chose a short one to minimize the logs.
--chatserver 192.168.1.124:2900
: Another bogus host to be used as a chatserver.

The other options don't really matter right now (as we don't have another side to talk to this will never be used). This generates the inital network traffic the client is performing to setup a connection to the server.

Once you trace the crypt and networking code (using wine as described below) you are able to trace what ever the client was doing.

Samstag, 11. Juli 2009

Listing the available options

While playing around with the bits and pieces to launch the client I accidentially got it to print all options. Here is a short list of all available commandline options of the game client.


You must specify an account name

-a, --account : <string>: Specifies the account name to logon with.
--authserverurl : Auth server URL for refreshing the GLS ticket.
--bugurl : <url> : Specifies the url that should be used for reporting bugs.
--chatserver : Specify the chat server to use.
--connspeed : <0.0-640.0>:Connection speed in Kilobits/sec for the server-client connection. 0 Defaults to speed searching
-r, --create : <name> : Character Name you would like to create/play
--dbwlib : <wlib DID> : Specifies the exact wlib did contained in the datfiles to use.
--debug : <32 bitfield>: Controls what kinds of debug outputs are enabled.
-f, --franchise : <string>: Specifies the franchise name.
--gametype : <string> : Specifies the game type used for supporturl and supportserviceurl.
--glsticket : Load gls ticket data from specified registry key.
-z, --glsticketdirect: <string>: Specify ticket data.
--glsticketlifetime: The lifetime of GLS tickets.
--HighResOutOfDate: Tells the client that the high resolution texture dat file was not updated. We will not switch into very high texture detail..
-h, --host : [host/IP]:Specifies where to find the server to talk to.
--keymap : <file> : Base file for the keymap. Will also look for <filename>c.extension and <filename>s.extension for meta keys
--language : <string>: Language to run the client in.
-m, --mps : <monster play session>.wc : Monster Play Session to start with
--nodbwlib : Tells the client not to use the database cache for locating the wlib. Instead, the wlib will be loaded directly from the hard drive.
--outport : <1-65535>: Specify the outgoing network port to use.
-p, --port : <1-65535>: Specify the server port to contact. See 'host'
--prefs : <string>: Specify the preferences file to use.
--resource-set : <string>: A comma separated list of available resource sets. The last set is the default.
--rodat : Opens the DAT files readonly
--safe : Force SAFE display settings.
-s, --specify : <race>,<class> : Race Class pair for a character you would like to create
--supportserviceurl: <url> : Specifies the url that should be used for auto submission of in game support tickets.
--supporturl : <url> : Specifies the url that should be used for in game support.
--surveyurl : <url> : URL to use for quest surveys
--usechat : Specify that the client should connect to a chat server.
--useexiturl : launch a browser using an exit url
-u, --user : <name> : Character Name you would like to play
--usetdm : Enable the download manager
--voicenetdelay : <int>: Specifies the voice network delay threshold in milliseconds.
--voiceoff : Disables the Voice chat system.
--webjournalurl : <url> : URL to use for the web journal
--wfilelog : <64-bitmask> : activates file logging for the specified weenie event types. Alternately, logtype enums seperated by ',' are enummapped and or'ed together.
--wprintlog : <64-bitmask> : activates print logging for the specified weenie event types. Alternately, logtype enums seperated by ',' are enummapped and or'ed together.

Samstag, 21. Februar 2009

Tracing the crypt API

I managed to get lotro to run on wine on linux. I pretty much used the steps described at lotros appdb entry and also did "winetricks directx9 because of "d3dx9_36.dll".

Here is the complete dump of that run (stripped down to the relevant trace to the crypt api):
Code:
trace:crypt:CryptAcquireContextA (0x5acfb4c, C982860F-EB79-4CFB-8EFC-6F0426F796B6, Microsoft Enhanced Cryptographic Provider v1.0, 1, 00000008)
trace:crypt:CryptAcquireContextW (0x5acfb4c, L"C982860F-EB79-4CFB-8EFC-6F0426F796B6", L"Microsoft Enhanced Cryptographic Provider v1.0", 1, 00000008)
trace:crypt:RSAENH_CPAcquireContext (phProv=0x4f4b008, pszContainer="C982860F-EB79-4CFB-8EFC-6F0426F796B6", dwFlags=00000008, pVTable=0x4f4b088)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000001)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000001)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000000)
trace:crypt:CryptImportKey (0x4f4aff8, 0x1020b30, 308, 0x0, 00000001, 0x582feac)
trace:crypt:RSAENH_CPImportKey (hProv=00000001, pbData=0x1020b30, dwDataLen=308, hPubKey=00000000, dwFlags=00000001, phKey=0x4f4b624)
trace:crypt:import_private_key installing key exchange key
trace:crypt:RSAENH_CPDestroyKey (hProv=00000001, hKey=ffffffff)
trace:crypt:CryptProtectData called
trace:crypt:report pPromptStruct: (nil)
trace:crypt:report dwFlags: 0x0000
trace:crypt:report pDataIn cbData: 308
trace:crypt:report pDataIn pbData @ 0x4f501c0:07,02,00,00,00,a4,00,00,52,53,41,32,00,02,00,00,01,00,00,00,ab,ef,fa,c6,7d,e8,de,fb,68,38,09,92,d9,42,7e,6b,89,9e,21,d7,52,1c,99,3c,17,48,4e,3a,44,02,f2,fa,74,57,da,e4,d3,c0,35,67,fa,6e,df,78,4c,75,3
trace:crypt:CryptProtectData szDataDescr: (nil)
trace:crypt:CryptAcquireContextW (0x33f98c, (null), L"Microsoft Enhanced Cryptographic Provider v1.0", 1, f0000000)
trace:crypt:RSAENH_CPAcquireContext (phProv=0x4f4b160, pszContainer=(null), dwFlags=f0000000, pVTable=0x4f4b170)
trace:crypt:fill_protect_data called
trace:crypt:CryptGenRandom (0x4f4b150, 0, 0x4f4b0e8)
trace:crypt:RSAENH_CPGenRandom (hProv=00000004, dwLen=0, pbBuffer=0x4f4b0e8)
trace:crypt:fill_protect_data &pInfo->salt cbData: 16
trace:crypt:fill_protect_data &pInfo->salt pbData @ 0x4f4b0e8:c8,00,11,00,b8,00,11,00,6f,00,73,00,6f,00,66,00
trace:crypt:CryptCreateHash (0x4f4b150, 0x8004, 0x0, 00000000, 0x33f86c)
trace:crypt:RSAENH_CPCreateHash (hProv=00000004, Algid=00008004, hKey=00000000, dwFlags=00000000, phHash=0x4f4b104)
trace:crypt:CryptHashData (0x4f4b100, 0x4f4b118, 7, 00000000)
trace:crypt:RSAENH_CPHashData (hProv=00000004, hHash=00000005, pbData=0x4f4b118, dwDataLen=7, dwFlags=00000000)
trace:crypt:CryptHashData (0x4f4b100, 0x7bfe56f4, 19, 00000000)
trace:crypt:RSAENH_CPHashData (hProv=00000004, hHash=00000005, pbData=0x7bfe56f4, dwDataLen=19, dwFlags=00000000)
trace:crypt:CryptHashData (0x4f4b100, 0x4f4b0e8, 16, 00000000)
trace:crypt:RSAENH_CPHashData (hProv=00000004, hHash=00000005, pbData=0x4f4b0e8, dwDataLen=16, dwFlags=00000000)
trace:crypt:CryptDeriveKey (0x4f4b150, 0x00006603, 0x4f4b100, 0x00a80001, 0x33f984)
trace:crypt:RSAENH_CPDeriveKey (hProv=00000004, Algid=26115, hBaseData=00000005, dwFlags=00a80001 phKey=0x4f4b134)
trace:crypt:RSAENH_CPGetHashParam (hProv=00000004, hHash=00000005, dwParam=00000002, pbData=0x33f618, pdwDataLen=0x33f7d0, dwFlags=00000000)
trace:crypt:CryptDestroyHash (0x4f4b100)
trace:crypt:RSAENH_CPDestroyHash (hProv=00000004, hHash=00000005)
trace:crypt:CryptCreateHash (0x4f4b150, 0x8004, 0x0, 00000000, 0x33f988)
trace:crypt:RSAENH_CPCreateHash (hProv=00000004, Algid=00008004, hKey=00000000, dwFlags=00000000, phHash=0x4f4b104)
trace:crypt:CryptEncrypt (0x4f4b130, 0x0, 1, 00000000, 0x4f501c0, 0x33f980, 0)
trace:crypt:RSAENH_CPEncrypt (hProv=00000004, hKey=00000006, hHash=00000000, Final=1, dwFlags=00000000, pbData=0x4f501c0, pdwDataLen=0x33f980, dwBufLen=0)
trace:crypt:CryptProtectData required encrypted storage: 312
trace:crypt:CryptEncrypt (0x4f4b130, 0x4f4b100, 1, 00000000, 0x4f50a28, 0x33f980, 312)
trace:crypt:RSAENH_CPEncrypt (hProv=00000004, hKey=00000006, hHash=00000005, Final=1, dwFlags=00000000, pbData=0x4f50a28, pdwDataLen=0x33f980, dwBufLen=312)
trace:crypt:RSAENH_CPHashData (hProv=00000004, hHash=00000005, pbData=0x4f50a28, dwDataLen=308, dwFlags=00000000)
trace:crypt:CryptProtectData &protect_data.cipher cbData: 312
trace:crypt:CryptProtectData &protect_data.cipher pbData @ 0x4f50a28:75,ed,86,25,73,e8,fb,9c,fa,98,dc,41,d2,0c,81,98,bf,ec,43,4b,48,95,af,ce,ec,19,11,80,17,ce,56,9c,04,7b,bd,03,cb,cd,a4,7f,43,50,e4,cb,43,b4,26,cb,44,41,b5,42,84,69,3b,e4,00,de,37,62,32,37,64,46,1a,d6,7
trace:crypt:convert_hash_to_blob called
trace:crypt:CryptGetHashParam (0x4f4b100, 4, 0x33f974, 0x33f86c, 00000000)
trace:crypt:RSAENH_CPGetHashParam (hProv=00000004, hHash=00000005, dwParam=00000004, pbData=0x33f974, pdwDataLen=0x33f86c, dwFlags=00000000)
trace:crypt:CryptGetHashParam (0x4f4b100, 2, 0x4f4af78, 0x33f86c, 00000000)
trace:crypt:RSAENH_CPGetHashParam (hProv=00000004, hHash=00000005, dwParam=00000002, pbData=0x4f4af78, pdwDataLen=0x33f86c, dwFlags=00000000)
trace:crypt:serialize called
trace:crypt:CryptDestroyHash (0x4f4b100)
trace:crypt:RSAENH_CPDestroyHash (hProv=00000004, hHash=00000005)
trace:crypt:CryptDestroyKey (0x4f4b130)
trace:crypt:RSAENH_CPDestroyKey (hProv=00000004, hKey=00000006)
trace:crypt:free_protect_data called
trace:crypt:CryptReleaseContext (0x4f4b150, 00000000)
trace:crypt:RSAENH_CPReleaseContext (hProv=00000004, dwFlags=00000000)
trace:crypt:CryptProtectData pDataOut cbData: 450
trace:crypt:CryptProtectData pDataOut pbData @ 0x4f50b68:01,00,00,00,57,69,6e,65,20,43,72,79,70,74,33,32,20,6f,6b,00,01,00,00,00,57,69,6e,65,20,43,72,79,70,74,33,32,20,6f,6b,00,00,00,00,00,02,00,00,00,00,00,03,66,00,00,a8,00,00,00,10,00,00,00,57,69,6e,65,2
trace:crypt:CryptProtectData returning ok
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000001)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000001)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb28, pdwDataLen=0x33fb84, dwFlags=00000000)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb24, 0x33fb80, 00000001)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb24, pdwDataLen=0x33fb80, dwFlags=00000001)
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb24, 0x33fb80, 00000000)
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb24, pdwDataLen=0x33fb80, dwFlags=00000000)
trace:crypt:CryptImportKey (0x4f4aff8, 0xe5f6b8, 148, 0x0, 00000001, 0x582fec0)
trace:crypt:RSAENH_CPImportKey (hProv=00000001, pbData=0xe5f6b8, dwDataLen=148, hPubKey=00000000, dwFlags=00000001, phKey=0x4f4b6ec)
trace:crypt:import_public_key installing public key
trace:crypt:RSAENH_CPDestroyKey (hProv=00000001, hKey=00000003)
trace:crypt:CryptProtectData called
trace:crypt:report pPromptStruct: (nil)
trace:crypt:report dwFlags: 0x0000
trace:crypt:report pDataIn cbData: 596
trace:crypt:report pDataIn pbData @ 0x4f50598:07,02,00,00,00,a4,00,00,52,53,41,32,00,04,00,00,01,00,01,00,4d,03,8d,d7,d8,a8,85,97,2d,04,de,57,e2,77,c4,bf,c6,13,15,0e,26,72,99,11,a1,cd,f3,8f,d5,5d,63,4c,f3,75,b7,62,9d,ee,e8,d5,c5,a7,0e,3c,9c,51,d
trace:crypt:CryptProtectData szDataDescr: (nil)
trace:crypt:CryptAcquireContextW (0x33f9b0, (null), L"Microsoft Enhanced Cryptographic Provider v1.0", 1, f0000000)
trace:crypt:RSAENH_CPAcquireContext (phProv=0x4f4b180, pszContainer=(null), dwFlags=f0000000, pVTable=0x4f4b190)
trace:crypt:fill_protect_data called
trace:crypt:CryptGenRandom (0x4f4b170, 0, 0x4f4b118)
trace:crypt:RSAENH_CPGenRandom (hProv=00000006, dwLen=0, pbBuffer=0x4f4b118)
trace:crypt:fill_protect_data &pInfo->salt cbData: 16
trace:crypt:fill_protect_data &pInfo->salt pbData @ 0x4f4b118:c8,00,11,00,b8,00,11,00,70,00,68,00,69,00,63,00
trace:crypt:CryptCreateHash (0x4f4b170, 0x8004, 0x0, 00000000, 0x33f890)
trace:crypt:RSAENH_CPCreateHash (hProv=00000006, Algid=00008004, hKey=00000000, dwFlags=00000000, phHash=0x4f4b134)
trace:crypt:CryptHashData (0x4f4b130, 0x4f4b148, 7, 00000000)
trace:crypt:RSAENH_CPHashData (hProv=00000006, hHash=00000005, pbData=0x4f4b148, dwDataLen=7, dwFlags=00000000)
trace:crypt:CryptHashData (0x4f4b130, 0x7bfe56f4, 19, 00000000)
trace:crypt:RSAENH_CPHashData (hProv=00000006, hHash=00000005, pbData=0x7bfe56f4, dwDataLen=19, dwFlags=00000000)
trace:crypt:CryptHashData (0x4f4b130, 0x4f4b118, 16, 00000000)
trace:crypt:RSAENH_CPHashData (hProv=00000006, hHash=00000005, pbData=0x4f4b118, dwDataLen=16, dwFlags=00000000)
trace:crypt:CryptDeriveKey (0x4f4b170, 0x00006603, 0x4f4b130, 0x00a80001, 0x33f9a8)
trace:crypt:RSAENH_CPDeriveKey (hProv=00000006, Algid=26115, hBaseData=00000005, dwFlags=00a80001 phKey=0x4f4af7c)
trace:crypt:RSAENH_CPGetHashParam (hProv=00000006, hHash=00000005, dwParam=00000002, pbData=0x33f63c, pdwDataLen=0x33f7f4, dwFlags=00000000)
trace:crypt:CryptDestroyHash (0x4f4b130)
trace:crypt:RSAENH_CPDestroyHash (hProv=00000006, hHash=00000005)
trace:crypt:CryptCreateHash (0x4f4b170, 0x8004, 0x0, 00000000, 0x33f9ac)
trace:crypt:RSAENH_CPCreateHash (hProv=00000006, Algid=00008004, hKey=00000000, dwFlags=00000000, phHash=0x4f4b134)
trace:crypt:CryptEncrypt (0x4f4af78, 0x0, 1, 00000000, 0x4f50598, 0x33f9a4, 0)
trace:crypt:RSAENH_CPEncrypt (hProv=00000006, hKey=00000007, hHash=00000000, Final=1, dwFlags=00000000, pbData=0x4f50598, pdwDataLen=0x33f9a4, dwBufLen=0)
trace:crypt:CryptProtectData required encrypted storage: 600
trace:crypt:CryptEncrypt (0x4f4af78, 0x4f4b130, 1, 00000000, 0x4f50f20, 0x33f9a4, 600)
trace:crypt:RSAENH_CPEncrypt (hProv=00000006, hKey=00000007, hHash=00000005, Final=1, dwFlags=00000000, pbData=0x4f50f20, pdwDataLen=0x33f9a4, dwBufLen=600)
trace:crypt:RSAENH_CPHashData (hProv=00000006, hHash=00000005, pbData=0x4f50f20, dwDataLen=596, dwFlags=00000000)
trace:crypt:CryptProtectData &protect_data.cipher cbData: 600
trace:crypt:CryptProtectData &protect_data.cipher pbData @ 0x4f50f20:37,4d,86,43,7b,79,12,11,08,e0,7d,84,98,81,59,51,c1,ca,16,8e,11,99,c0,b5,18,52,ab,94,43,9e,33,7a,69,67,e8,96,1b,22,35,2a,19,5f,5b,d3,de,b9,c2,0d,c8,d8,2f,24,19,5f,55,9c,08,aa,87,da,17,b6,9c,55,6d,e6,1
trace:crypt:convert_hash_to_blob called
trace:crypt:CryptGetHashParam (0x4f4b130, 4, 0x33f998, 0x33f890, 00000000)
trace:crypt:RSAENH_CPGetHashParam (hProv=00000006, hHash=00000005, dwParam=00000004, pbData=0x33f998, pdwDataLen=0x33f890, dwFlags=00000000)
trace:crypt:CryptGetHashParam (0x4f4b130, 2, 0x4f4b148, 0x33f890, 00000000)
trace:crypt:RSAENH_CPGetHashParam (hProv=00000006, hHash=00000005, dwParam=00000002, pbData=0x4f4b148, pdwDataLen=0x33f890, dwFlags=00000000)
trace:crypt:serialize called
trace:crypt:CryptDestroyHash (0x4f4b130)
trace:crypt:RSAENH_CPDestroyHash (hProv=00000006, hHash=00000005)
trace:crypt:CryptDestroyKey (0x4f4af78)
trace:crypt:RSAENH_CPDestroyKey (hProv=00000006, hKey=00000007)
trace:crypt:free_protect_data called
trace:crypt:CryptReleaseContext (0x4f4b170, 00000000)
trace:crypt:RSAENH_CPReleaseContext (hProv=00000006, dwFlags=00000000)
trace:crypt:CryptProtectData pDataOut cbData: 738
trace:crypt:CryptProtectData pDataOut pbData @ 0x4f51180:01,00,00,00,57,69,6e,65,20,43,72,79,70,74,33,32,20,6f,6b,00,01,00,00,00,57,69,6e,65,20,43,72,79,70,74,33,32,20,6f,6b,00,00,00,00,00,02,00,00,00,00,00,03,66,00,00,a8,00,00,00,10,00,00,00,57,69,6e,65,2
trace:crypt:CryptProtectData returning ok
trace:crypt:CryptGenKey (0x4f4aff8, 26625, 00000001, 0x582fed4)
trace:crypt:RSAENH_CPGenKey (hProv=00000001, aiAlgid=26625, dwFlags=00000001, phKey=0x4f4b704)
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, (nil), 0x79f1c89c)
trace:crypt:RSAENH_CPExportKey (hProv=00000001, hKey=00000006, hPubKey=00000004, dwBlobType=00000001, dwFlags=00000000, pbData=(nil),pdwDataLen=0x79f1c89c)
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, 0x6e15be0, 0x79f1c89c)
trace:crypt:RSAENH_CPExportKey (hProv=00000001, hKey=00000006, hPubKey=00000004, dwBlobType=00000001, dwFlags=00000000, pbData=0x6e15be0,pdwDataLen=0x79f1c89c)
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, (nil), 0x79f1c87c, 265)
trace:crypt:RSAENH_CPEncrypt (hProv=00000001, hKey=00000006, hHash=00000000, Final=1, dwFlags=00000000, pbData=(nil), pdwDataLen=0x79f1c87c, dwBufLen=265)
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, 0x6e1fe34, 0x79f1c880, 265)
trace:crypt:RSAENH_CPEncrypt (hProv=00000001, hKey=00000006, hHash=00000000, Final=1, dwFlags=00000000, pbData=0x6e1fe34, pdwDataLen=0x79f1c880, dwBufLen=265)
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, (nil), 0x79f1c944)
trace:crypt:RSAENH_CPExportKey (hProv=00000001, hKey=00000006, hPubKey=00000004, dwBlobType=00000001, dwFlags=00000000, pbData=(nil),pdwDataLen=0x79f1c944)
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, 0x6e4ea48, 0x79f1c944)
trace:crypt:RSAENH_CPExportKey (hProv=00000001, hKey=00000006, hPubKey=00000004, dwBlobType=00000001, dwFlags=00000000, pbData=0x6e4ea48,pdwDataLen=0x79f1c944)
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, (nil), 0x79f1c924, 265)
trace:crypt:RSAENH_CPEncrypt (hProv=00000001, hKey=00000006, hHash=00000000, Final=1, dwFlags=00000000, pbData=(nil), pdwDataLen=0x79f1c924, dwBufLen=265)
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, 0x6e58c9c, 0x79f1c928, 265)
trace:crypt:RSAENH_CPEncrypt (hProv=00000001, hKey=00000006, hHash=00000000, Final=1, dwFlags=00000000, pbData=0x6e58c9c, pdwDataLen=0x79f1c928, dwBufLen=265)
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, (nil), 0x79f1c944)
trace:crypt:RSAENH_CPExportKey (hProv=00000001, hKey=00000006, hPubKey=00000004, dwBlobType=00000001, dwFlags=00000000, pbData=(nil),pdwDataLen=0x79f1c944)
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, 0x6e4ea48, 0x79f1c944)
trace:crypt:RSAENH_CPExportKey (hProv=00000001, hKey=00000006, hPubKey=00000004, dwBlobType=00000001, dwFlags=00000000, pbData=0x6e4ea48,pdwDataLen=0x79f1c944)
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, (nil), 0x79f1c924, 265)
trace:crypt:RSAENH_CPEncrypt (hProv=00000001, hKey=00000006, hHash=00000000, Final=1, dwFlags=00000000, pbData=(nil), pdwDataLen=0x79f1c924, dwBufLen=265)
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, 0x6e58c9c, 0x79f1c928, 265)
trace:crypt:RSAENH_CPEncrypt (hProv=00000001, hKey=00000006, hHash=00000000, Final=1, dwFlags=00000000, pbData=0x6e58c9c, pdwDataLen=0x79f1c928, dwBufLen=265)
So, I hope you brought some time sine we are going through these calls ... one by one.

Code:
trace:crypt:CryptAcquireContextA (0x5acfb4c, C982860F-EB79-4CFB-8EFC-6F0426F796B6, Microsoft Enhanced Cryptographic Provider v1.0, 1, 00000008)
  • 0x5acfb4c: memory offset where the generated crypt provider will be placed, uninteresting
  • C982860F-EB79-4CFB-8EFC-6F0426F796B6: the key containers name ... a GUID which I have no info about. I think it's a GUID chosen by Codemasters to identify their container.
  • Microsoft Enhanced Cryptographic Provider v1.0: this is MS_ENHANCED_PROV
  • 1: this is PROV_RSA_FULL
  • 00000008: this is CRYPT_NEWKEYSET

Code:
trace:crypt:CryptAcquireContextW (0x5acfb4c, L"C982860F-EB79-4CFB-8EFC-6F0426F796B6", L"Microsoft Enhanced Cryptographic Provider v1.0", 1, 00000008)
Wine internally forwards ASCII calls to theid unicode equivalents. you can ignore this.

Code:
trace:crypt:RSAENH_CPAcquireContext (phProv=0x4f4b008, pszContainer="C982860F-EB79-4CFB-8EFC-6F0426F796B6", dwFlags=00000008, pVTable=0x4f4b088)
This is wines internal call the actual cryptographic provider, RSA. Not much of an interest here.

Code:
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000001)
  • 0x4f4aff8: This is the provider aquired by CryptAcquireContextA. Interesting is that in CryptAcquireContextA it was 0x5acfb4c while here it is 0x4f4aff8. Looks like it was copied in memory to be safe of modifications (or I'm just misreading the log here).
  • 22: is 0x16 in hex, so PP_ENUMALGS_EX
  • 0x33fb00: an instance of the PP_ENUMALGS_EX structure
  • 0x33fb5c: pointer to the length of the PP_ENUMALGS_EX structure, out param in this case
  • 00000001: first call, so CRYPT_FIRST must be set

Code:
trace:crypt:RSAENH_CPGetProvParam (hProv=00000001, dwParam=00000016, pbData=0x33fb00, pdwDataLen=0x33fb5c, dwFlags=00000001)
  • 00000001: internal handle to the provider
  • 16: PP_PROVTYPE
  • 0x33fb00: out pointer for data
  • 0x33fb5c: out pointer for data length
  • 00000001: CRYPT_FIRST
Forward call to the RSA provider. These calls are pretty much straight forward and not really relevant for our output. I'll skip these from now on and only come back to them if I think they are interesting.

Code:
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb00, 0x33fb5c, 00000000)
Exactly the same call as the CryptGetProvParam above without the CRYPT_FIRST flag set. No idea why one would call that multiple times. Maybe CryptGetProvParam returns information about a different algo each time? And maybe they search for a specific one? No idea.

Code:
trace:crypt:CryptImportKey (0x4f4aff8, 0x1020b30, 308, 0x0, 00000001, 0x582feac)
  • 0x4f4aff8: handle to the CSP, we already know from the calls before
  • 0x1020b30: dang, the public key as a blob. need to figure out where it comes from. really interesting call. Definitely need to find out what is in there.
  • 308: length of the key
  • 0x0: hmm, really interesting! is set to zero which means: they key in not encrypted. plain public key in memory.
  • 00000001: looks like CRYPT_EXPORTABLE
  • 0x582feac: handle to the imported key.

Code:
trace:crypt:CryptProtectData called
trace:crypt:report pPromptStruct: (nil)
trace:crypt:report dwFlags: 0x0000
trace:crypt:report pDataIn cbData: 308
trace:crypt:report pDataIn pbData @ 0x4f501c0:07,02,00,00,00,a4,00,00,52,53,41,32,00,02,00,00,01,00,00,00,ab,ef,fa,c6,7d,e8,de,fb,68,38,09,92,d9,42,7e,6b,89,9e,21,d7,52,1c,99,3c,17,48,4e,3a,44,02,f2,fa,74,57,da,e4,d3,c0,35,67,fa,6e,df,78,4c,75,3
trace:crypt:CryptProtectData szDataDescr: (nil)
Strange looking trace, i guess.
  • pDataIn: data to be encrypted.
  • szDataDescr: no description of the pDataIn given. totally ok.
  • pOptionalEntropy: missing this one .... means no extra password or entropy given
  • pPromptStruct: no prompts given. totally ok.
  • dwFlags: no special crypt protect, good.
  • pDataOut: encrypted data ... pretty much irrelevant
So at this point the valuable data from pbData was symetric encrypted. The wine source states that this is only for host encryption where you cant trust other users, see dlls/crypt32/protectdata.c. It's worth noting that this call ends with
Code:
trace:crypt:CryptProtectData returning ok
while the stuff in between is wines interal calls.

Code:
trace:crypt:CryptGetProvParam (0x4f4aff8, 22, 0x33fb28, 0x33fb84, 00000001)
Looks like they are searching a algo or a key again.

Code:
trace:crypt:CryptImportKey (0x4f4aff8, 0xe5f6b8, 148, 0x0, 00000001, 0x582fec0)
  • 0x4f4aff8: handle to the CSP, as always
  • 0xe5f6b8: again, the key to import
  • 148: short one this time
  • 0x0: not encrypted
  • 00000001: CRYPT_EXPORTABLE
  • 0x582fec0: handle to the imported key.

Code:
trace:crypt:CryptProtectData called
trace:crypt:report pPromptStruct: (nil)
trace:crypt:report dwFlags: 0x0000
trace:crypt:report pDataIn cbData: 596
trace:crypt:report pDataIn pbData @ 0x4f50598:07,02,00,00,00,a4,00,00,52,53,41,32,00,04,00,00,01,00,01,00,4d,03,8d,d7,d8,a8,85,97,2d,04,de,57,e2,77,c4,bf,c6,13,15,0e,26,72,99,11,a1,cd,f3,8f,d5,5d,63,4c,f3,75,b7,62,9d,ee,e8,d5,c5,a7,0e,3c,9c,51,d
trace:crypt:CryptProtectData szDataDescr: (nil)
Again protects the key against access from other users.

Code:
trace:crypt:CryptGenKey (0x4f4aff8, 26625, 00000001, 0x582fed4)
  • 0x4f4aff8: handle to the CSP
  • 26625: this is CALG_RC4, stream encryption
  • 00000001: CRYPT_EXPORTABLE
  • 0x582fed4: handle where the key will be stored

Code:
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, (nil), 0x79f1c89c)
  • 0x4f4b700: handle to the key that will be exported.
  • 0x4f4b6e8: this is the handle to a public key used for encryption of 0x4f4b700
  • 1: SIMPLEBLOB
  • 00000000: no dwParam
  • (nil): ok, we have no idea how large the exported key will be. this is a dry run to determine the size
  • 0x79f1c89c: size will be put here
This was only a dry run to find out how large the exported key will be. Now the client will allocate a buffer and really export the key.

Code:
trace:crypt:CryptExportKey (0x4f4b700, 0x4f4b6e8, 1, 00000000, 0x6e15be0, 0x79f1c89c)
  • 0x4f4b700: same as above
  • 0x4f4b6e8: same as above
  • 1: same as above
  • 00000000: same as above
  • 0x6e15be0: buffer where the encrypted key will end in
  • 0x79f1c89c: same as above

Code:
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, (nil), 0x79f1c87c, 265)
  • 0x4f4b700: handle to the key that will be used for encryption
  • 0x0: encrypted data will not be hashed
  • 1: final is set so the data passed in this call is the complete data
  • 00000000: no dwFlags
  • (nil): again, dry run. will put the size into the next param
  • 0x79f1c87c: size of the buffer necessary to store the encrypted result
  • 265: means we want to encrypt 265 bytes
Dry run is through, now on the the encryption with the buffer.

Code:
trace:crypt:CryptEncrypt (0x4f4b700, 0x0, 1, 00000000, 0x6e1fe34, 0x79f1c880, 265)
  • 0x4f4b700: same as a above
  • 0x0: same as a above
  • 1: same as a above
  • 00000000: same as a above
  • 0x6e1fe34: buffer containing the plain text and containing the ciphered text after this function passed
  • 0x79f1c87c: size of ciphered data
  • 265: same as above

Ok, that's pretty much it. I'll try to explain in normal text what happens here.
  • The client is loading 2 stored keys here. I'm not sure why there are 2 but I guess that at least on of them is the public key RSA of the server.
  • The client creates a key that can be used for fast encryption (RC4 based).
  • This key is exported and encrypted with the public key of the server. The result is transfered to the server where it decrypts the RC4 key with his private RSA key.
  • I guess the next logical step would be the server creating so "welcome" packet and encrypting it with RC4. The client would then check if the welcome message from the server was valid (which means: the server had the private RSA key necessary for decrypting the RC4).

The big issue: We are not the server and we don't own the private key. This means there is no possibility to get this private key. So where is the point we could attack to break the encrpytion? Well, what if the client wouldn't use the original public key for encryption, but one that we created ourselves? This way we would have the public and the private key. Once we have the RC4 key we can encrypt and decrypt any of the data.

Next to that we need the possibility to decrypt the traffic between client and server of a production system. We could hack the CryptGenKey to dump the key it creates. This would enable us to look into the traffic that is happening between the client and the server.

Hope I didn't bore you to death. Thanks for reading.