• Welcome to Valhalla Legends Archive.
 

Need help. Logging BNet... [NOT solved]

Started by z-stars, July 22, 2004, 01:47 PM

Previous topic - Next topic
|

z-stars

I'm trying to make a bot in C++. Using bnetdocs I've built&sent / received the first packets, but now I gotta send SID_AUTH_CHECK (0x51) and I dont know what to do. I've read that you have to encode the CDKEy and some more values, but I dont know how to do that. Can anyone post some source, or a link, or point me in the right direction plz? Thx in advance.

MyndFyre

QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

z-stars

Isn't there any C++ function to make the packet 0x51 yourself or something?

OnlyMeat

Quote from: Myndfyre on July 22, 2004, 02:53 PM
http://www.valhallalegends.com/yoni/BNLSProtocolSpec.txt

Thats assuming that he wants to use another connection layer as apposed to connecting directly, it sounds like he wants to do the hashing etc himself which would make bnls inappropriate.

I suggest looking at the prolix source code it's public and the latest versions are pretty accurate, although you may need to modify the rotate functions slightly for big endian.

I think you can access the cvs here :-
http://prolix.sourceforge.net/

UserLoser.

#4
Quote from: z-stars on July 22, 2004, 03:05 PM
Isn't there any C++ function to make the packet 0x51 yourself or something?

You should use Battle.snp to do all your hashing, and CDKey decoding.  If you're interested, let me know.  Less code to write, and probably faster :)

z-stars

#5
Yeah I'd preffer to do the hashing myself, and what is that Battle.snp thing?

OnlyMeat

Quote from: UserLoser. on July 22, 2004, 04:10 PM
Quote from: z-stars on July 22, 2004, 03:05 PM
Isn't there any C++ function to make the packet 0x51 yourself or something?

You should use Battle.snp to do all your hashing, and CDKey decoding.  If you're interested, let me know.  Less code to write, and probably faster :)

What happends if the oridinal position//function name changes in the snp? i think that would leed to linkage problems if the file is changed.

I personally think thats just the same as using bnls it relies on others peoples code which can go wrong and if it does for any reason then you cant fix it yourself, my personal preference is to do the hashing myself because it keeps things clean and simple :)

UserLoser.

#7
Quote from: z-stars on July 22, 2004, 04:35 PM
Yeah I'd preffer to do the hashing myself, and what is that Battle.snp thing?

Basically, you call functions in Battle.snp to do the hashing and CDKey decoding for you.

Note, this is from Starcraft's 1.09 Battle.snp, since the newer one appears to have the CDKey decoding not in it's own function.

Here's an example:


const DWORD HashFunction = 0x19012400;

void HashData(DWORD length, LPVOID outbuf, LPVOID tohash)
{
   DWORD len = length;
   __asm {
      push ecx
      push edx
      mov ecx, outbuf
      mov edx, tohash
      push len
      call HashFunction
      pop edx
      pop ecx
   }
}

If you wanted to create an account, it requires a 5 DWORD password hash.  What you could do is:


char* outbuf = new char[20];
HashData(strlen(AccountName), outbuf, AccountName);

<insert outbuf into packet buffer along with account name, send packet>

delete [] outbuf;


If you wanted to log onto your account, you would do a double hash; which includes a client key and server key (server key from 0x50):



DWORD dwPasswordHash[7];
dwPasswordHash[0] = GetTickCount();
dwPasswordHash[1] = ServerSessionKey;
HashData(strlen(Password), dwPasswordHash+0x2, Password);
HashData(28, dwPasswordHash, dwPasswordHash+0x2);



Now, for CDKey decoding and hashing:


const DWORD DecodeCDKeyFunction = 0x19019AB0;

void RunCDkeyDecode(DWORD *Product, DWORD *CDKeyVal1, DWORD *CDKeyVal2, const char *CDKey)
{

   __asm
   {
      push ecx
      push edx
      mov ecx, CDKey
      mov edx, Product
      push CDKeyVal2
      push CDKeyVal1
      call DecodeCDKeyFunction
      pop edx
      pop ecx
   }
}


Use it like so:

DWORD *dwProduct = (DWORD*)malloc(4);
DWORD *dwPrivate = (DWORD*)malloc(4);
DWORD *dwPublic = (DWORD*)malloc(4);
RunCDkeyDecode(&dwProduct, &dwPublic, &dwPrivate, "my sc/d2/w2 cdkey");

Hash the CDKey values like so:


DWORD dwHashBuffer[6];
DWORD dwOutBuffer[5];
dwHashBuffer[0] = GetTickCount();
dwHashBuffer[1] = ServerSessionKey;
dwHashBuffer[2] = dwProduct;
dwHashBuffer[3] = dwPublic;
dwHashBuffer[4] = 0;
dwHashBuffer[5] = dwPrivate;

HashData(24, dwOutBuffer, dwHashBuffer);


And once again, just insert dwOutBuffer into your buffer for 0x51, along with the cdkey values listed on bnetdocs, then just send it to battle.net.


Hope there isn't any mistakes, I haven't done any of this in a while :)

Enjoy :)

z-stars

Erm yeah I want to do the hashing myself too, but I dont know how ^^
BTW, I have found this source, does anyone know if it works or how to use it?


/* Battle.net Version Check Source Code © YobGuls ([email protected]) 2001
* This is the equivalent of the CheckVersion() function that appears
* in the IX86VER?.DLL files that are used as part of the Battle.net
* connection procedure
* Thanks a lot to Onlyer ([email protected]) for explaining the basic
* parts of this function to me, it made it a lot easier
* Also, Skywing provided the additional checksum keys for certain DLLs */

#include <windows.h>
#include <stdlib.h>#include <stdio.h>


/* These are special keys that vary depending upon which DLL it is that you're using
* They're used to initialize the first variable (A) as part of the checksum algorithm */
/* Thanks to Skywing for the additional checksum keys */

DWORD dwMpqChecksumKeys[] =
{ 0xE7F4CB62lu, 0xF6A14FFClu, 0xAA5504AFlu, 0x871FCDC2lu, 0x11BF6A18lu, 0xC57292E6lu,
 0x7927D27Elu, 0x2FEC8733lu };

/* CheckRevision function
* This function takes in three file names and a versioning string.  The file names refer
* to the main EXE and DLL files that are used by the game.  Usually this includes the game's
* main EXE file (like Starcraft.exe) and the Battle.net client DLL (Battle.snp for Starcraft
* or Bnclient.dll for Diablo 2).  In the actual DLLs, the first argument is ignored and
* GetModuleFileName(NULL) is used instead.  The versioning string is a list of random
* variables and operations that tell how the checksum is performed.
* The function will write two 32-bit DWORDs as output.  The first is the version information
* taken from the actual resource in the main EXE file.  The second is the result of the
* checksum function. */
/* NOTE:  I added the MPQ file name argument, it's not in the real function of course.  Set
* that to the file that Battle.net specifies in the connection, like "IX86VER0.MPQ".  You
* don't actually have to download this file, the algorithm is all in here. */

BOOL CheckRevision(LPCTSTR lpszFileName1, LPCTSTR lpszFileName2,
                  LPCTSTR lpszFileName3, LPCTSTR lpszValueString,
                  DWORD * lpdwVersion, DWORD * lpdwChecksum,
                  LPSTR lpExeInfoString, LPCTSTR lpszMpqFileName)
{
  HANDLE hFile, hFileMapping;
  char * s, lpszFileName[256], cOperations[16];
  int nHashFile, nVariable1[16], nVariable2[16], nVariable3[16], nVariable,
      i, k, nHashOperations;
  DWORD dwTotalSize, dwSize, j, dwBytesRead, dwVariables[4], dwMpqKey,
        * lpdwBuffer;
  LPSTR lpszFileNames[3];
  FILETIME ft;
  SYSTEMTIME st;
  LPBYTE lpbBuffer;
  VS_FIXEDFILEINFO * ffi;

  s = strchr((char *) lpszMpqFileName, '.');
  if (s == NULL)
     return FALSE;
  nHashFile = (int) (*(s - 1) - '0');
  if (nHashFile > 7 || nHashFile < 0)
     return FALSE;
  dwMpqKey = dwMpqChecksumKeys[nHashFile];
  lpszFileNames[0] = (LPSTR) lpszFileName1;
  lpszFileNames[1] = (LPSTR) lpszFileName2;
  lpszFileNames[2] = (LPSTR) lpszFileName3;
  s = (char *) lpszValueString;
  while (*s != '\0')
  {
     if (isalpha(*s))
        nVariable = (int) (toupper(*s) - 'A');
     else
     {
        nHashOperations = (int) (*s - '0');
        s = strchr(s, ' ');
        if (s == NULL)
           return FALSE;
        s++;
        break;
     }
     if (*(++s) == '=')
        s++;
     dwVariables[nVariable] = atol(s);
     s = strchr(s, ' ');
     if (s == NULL)
        return FALSE;
     s++;
  }
  for (i = 0; i < nHashOperations; i++)
  {
     if (!isalpha(*s))
        return FALSE;
     nVariable1[i] = (int) (toupper(*s) - 'A');
     if (*(++s) == '=')
        s++;
     if (toupper(*s) == 'S')
        nVariable2[i] = 3;
     else
        nVariable2[i] = (int) (toupper(*s) - 'A');
     cOperations[i] = *(++s);
     s++;
     if (toupper(*s) == 'S')
        nVariable3[i] = 3;
     else
        nVariable3[i] = (int) (toupper(*s) - 'A');
     s = strchr(s, ' ');
     if (s == NULL)
        break;
     s++;
  }
  dwVariables[0] ^= dwMpqKey;
  for (i = 0; i < 3; i++)
  {
     if (lpszFileNames[i][0] == '\0')
        continue;
     hFile = CreateFile(lpszFileNames[i],
                        GENERIC_READ,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);
     if (hFile == (HANDLE) INVALID_HANDLE_VALUE)
        return FALSE;
     hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
     if (hFileMapping == NULL)
     {
        CloseHandle(hFile);
        return FALSE;
     }
     lpdwBuffer = (LPDWORD) MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
     if (lpdwBuffer == NULL)
     {
        CloseHandle(hFileMapping);
        CloseHandle(hFile);
        return FALSE;
     }
     if (i == 0)
     {
        GetFileTime(hFile, &ft, NULL, NULL);
        FileTimeToSystemTime(&ft, &st);
        dwTotalSize = GetFileSize(hFile, NULL);
     }
     dwSize = (GetFileSize(hFile, NULL) / 1024lu) * 1024lu;
     for (j = 0; j < (dwSize / 4lu); j++)
     {
        dwVariables[3] = lpdwBuffer[j];
        for (k = 0; k < nHashOperations; k++)
        {
           switch (cOperations[k])
           {
              case '+':
                 dwVariables[nVariable1[k]] = dwVariables[nVariable2[k]] +
                                              dwVariables[nVariable3[k]];
                 break;
              case '-':
                 dwVariables[nVariable1[k]] = dwVariables[nVariable2[k]] -
                                              dwVariables[nVariable3[k]];
                 break;
              case '^':
                 dwVariables[nVariable1[k]] = dwVariables[nVariable2[k]] ^
                                              dwVariables[nVariable3[k]];
                 break;
              default:
                 return FALSE;
           }
        }
     }
     UnmapViewOfFile(lpdwBuffer);
     CloseHandle(hFileMapping);
     CloseHandle(hFile);
  }
  strcpy(lpszFileName, lpszFileName1);
  dwSize = GetFileVersionInfoSize(lpszFileName, &dwBytesRead);
  lpbBuffer = (LPBYTE) VirtualAlloc(NULL, dwSize, MEM_COMMIT,
                                    PAGE_READWRITE);
  if (lpbBuffer == NULL)
     return FALSE;
  if (GetFileVersionInfo(lpszFileName, NULL, dwSize, lpbBuffer) == FALSE)
     return FALSE;
  if (VerQueryValue(lpbBuffer, "\\", (LPVOID *) &ffi, (PUINT) &dwSize) == FALSE)
     return FALSE;
  *lpdwVersion = ((HIWORD(ffi->dwProductVersionMS) & 0xFF) << 24) |
                 ((LOWORD(ffi->dwProductVersionMS) & 0xFF) << 16) |
                 ((HIWORD(ffi->dwProductVersionLS) & 0xFF) << 8) |
                 (LOWORD(ffi->dwProductVersionLS) & 0xFF);
  VirtualFree(lpbBuffer, 0lu, MEM_RELEASE);
  s = (char *) &lpszFileName[strlen(lpszFileName)-1];
  while (*s != '\\' && s > (char *) lpszFileName)
     s--;
  s++;
  sprintf(lpExeInfoString,
          "%s %02u/%02u/%02u %02u:%02u:%02u %lu",
          s,
          st.wMonth,
          st.wDay,
          st.wYear % 100,
          st.wHour,
          st.wMinute,
          st.wSecond,
          dwTotalSize);
  *lpdwChecksum = dwVariables[2];
  return TRUE;
}


z-stars

Ops I submit my post at the same time than UserLoser. It looks well but how would I do that using d2 lod?

OnlyMeat

ye pretty much everyone derived their code from that i think it works pretty well.

if you want some working classes for hashing and version extraction in c++ pm me and we can talk.

z-stars

How do I actually use the code posted above? I mean, in packet 0x51 each time I log to bnet with d2, it sends different Client Token, Exe Hash, and different CdKey Data Hashes. How do I get these values?

OnlyMeat

#12
Quote from: z-stars on July 22, 2004, 05:29 PM
How do I actually use the code posted above? I mean, in packet 0x51 each time I log to bnet with d2, it sends different Client Token, Exe Hash, and different CdKey Data Hashes. How do I get these values?

when the server sends the 0x50 packet to you it contains the session key which is basically the encryption key, this vaue is then hashed with a salt ( random value ) and your cdkeys.

Note for d2 Exp you need to construct 2 hash sets one for each cdkey.

This is the function i wrote in my abstract base class for all my bot clients to handle 0x50:-


BOOL CBot::CreateAuthPacket(AUTHCHECK_INFO *pAuthInfo, CPacket& Packet)
{
   CNetEvent ne;
   ne.eEvent = EID_AUTHKEY;

   PumpMessage(&ne);

   //////////////////////////////////////////////////////////////////////////////////
   // Extract session variables
   m_SessionKey = *(UINT *)(pAuthInfo->pszAuthData  + 0x04);
   m_SessionNum = *(UINT *)(pAuthInfo->pszAuthData + 0x08);

   //////////////////////////////////////////////////////////////////////////////////
   // Extract equation variables
   char *pszHashMpq = (pAuthInfo->pszAuthData + 0x14);
   char *pszHashEqu = (pAuthInfo->pszAuthData + (strlen(pszHashMpq) + 0x15));
   int  nMpq = *(strstr(pszHashMpq, ".") - 1) - '0';
   
   ///////////////////////////////////////////////////////////////////////////////////
   // Validate hash files

   CVerCheck VerCheck;
   UINT nVersion, nChecksum;
   char szExeInfo[_MAX_FNAME];

   if (!ValidateBinaries(pAuthInfo->lpHashFiles[0], pAuthInfo->lpHashFiles[1],
         pAuthInfo->lpHashFiles[2]))
      CGenericException::ThrowException(IDS_ERROR_BINARIES);

   ///////////////////////////////////////////////////////////////////////////////////
   // Do version check
   VerCheck.CheckRevision(pAuthInfo->lpHashFiles[0], pAuthInfo->lpHashFiles[1],
      pAuthInfo->lpHashFiles[2], pszHashEqu, (LPDWORD)&nVersion, (LPDWORD)&nChecksum,
      szExeInfo, pszHashMpq);

   CCDKey CDKey;
   CEncryption Encryption;
   char szKeyBuf[MAX_CDKEY];
   UINT a, b, c;
   UINT hashbuf[6], hash[5];
   UINT nSeed = GetRand();         // Cookie

   ///////////////////////////////////////////////////////////////////////////////////
   // Fill-in default version info
   Packet   << nSeed            // Seed
         << nVersion             // Client Version
         << nChecksum            // Checksum from files
         << pAuthInfo->nCDKeys   // No. cd-keys
         << (UINT)0x00;         // Spawn (BOOL)

   ///////////////////////////////////////////////////////////////////////////////////
   // Hash cd-key data
   for ( UINT i=0; i < pAuthInfo->nCDKeys; i++ )
   {
      memset(szKeyBuf,0,MAX_CDKEY);      // Reset buffer

      // Extract cd-key variables
      strcpy(szKeyBuf, pAuthInfo->lpCDKeys[i]);
      CDKey.GetCDVars(szKeyBuf, &a, &b, &c);
      
      UINT nKeylength = strlen(szKeyBuf);

      Packet  << nKeylength            // CD-Key length
            << a                  // Product id
            << b                  // CD-Key val 1
            << (UINT)0x00;            // ?

      // Hash cd-key data
      hashbuf[0] = nSeed;            // Seed
      hashbuf[1] = m_SessionKey;      // Session key
      hashbuf[2] = a;
      hashbuf[3] = b;
      hashbuf[4] = 0;
      hashbuf[5] = c;
      
      // Use correct hash rotation
      Encryption.Sha1_HashB( (char *)hashbuf, 6*4, (char *)hash);
      Packet  << hash[0]
            << hash[1]
            << hash[2]
            << hash[3]
            << hash[4];
   }
   
   /////////////////////////////////////////////////////////////
   // Insert exe/reg info
   Packet  << szExeInfo
         << (BYTE)0x00
         << m_sReg
         << (BYTE)0x00;

   return TRUE;
}

UserLoser.

#13
Quote from: z-stars on July 22, 2004, 05:29 PM
How do I actually use the code posted above? I mean, in packet 0x51 each time I log to bnet with d2, it sends different Client Token, Exe Hash, and different CdKey Data Hashes. How do I get these values?

The Client token is different because it uses GetTickCount() like I posted above for that value.  The hash is different because it uses the server key value and a client key (client uses GetTickCount(), but you can use whatever you want) like posted above.  The only hashes that will ever be the same is the create account hash.  You can use starcraft's battle.snp if you want for all your hashing/cdkey decoding for d2.  Or, you can do it your own way.

z-stars

I'll try all that tomorrow, I go to sleep. Thanks for your help ppl.

|