Password Algorithms: Google Talk


Lots of people that use Gmail use Google Talk
It functions pretty much like any other IM application such as those available from MSN, Yahoo, AOL

OPSWAT reported in June 2011 that Google Talk accounts for 3.56% of market share in Instant Messaging.

That’s not a big percentage of users but the algorithm described here is probably used in other google applications.


If the user chooses to ‘Remember Password’ an account entry will be created in NTUSER.DAT
So for example if the google id is the registry entry would be

HKEY_CURRENT_USER\Software\Google\Google Talk\Accounts\

Password values are stored under a string value called pw
Without showing string for my own password, it looks something like


The value itself is much much longer but I just want to give you an idea of what to look for.
It doesn’t use any conventional encoding algorithm like Base64.


After some digging around in the binaries, Gtalk first initializes some entropy using a static key, the domain and username.

DWORD gtalk_entropy[4];
DWORD static_key[4] = { 0x69F31EA3, 0x1FD96207, 0x7D35E91E, 0x487DD24F };

 * Retrieve the current domainusername from thread or process token
 * return TRUE if successful
BOOL GetUserInfo(std::wstring &domain, std::wstring &username) {
    HANDLE hToken;
    DWORD dwTokenSize = 0, dwUserName = 64, dwDomain = 64;
    WCHAR UserName[64], Domain[64];
    SID_NAME_USE peUse;
    PSID pSid = NULL;
    BOOL bResult = FALSE;

    OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken);

    if (GetLastError() == ERROR_NO_TOKEN) {
      if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
        return FALSE;

    if (!GetTokenInformation(hToken, TokenUser, 0, 0, &dwTokenSize)) {
      if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
        pSid = new BYTE[dwTokenSize];
        if (pSid != NULL) {
          if (GetTokenInformation(hToken, TokenUser, pSid, dwTokenSize, &dwTokenSize)) {
            bResult = LookupAccountSid(NULL, reinterpret_cast(pSid)->User.Sid,
                UserName, &dwUserName, Domain, &dwDomain, &peUse);
            if (bResult) {
              domain = Domain;
              username = UserName;
          delete []pSid;
    return bResult;

All the above code does is retrieve from the process/thread security token the domain and username.

The seed value appears to be derived from a random number generator developed
by R.Park and K.Miller, however this is merely speculation as no generator exists in the binary itself.
48271 and 69621 are common multiplier values for RNG.

 * initialize the entropy that will used to encrypt/decrypt passwords
BOOL init_entropy() {
    std::wstring domain, username;

    BOOL bResult = GetUserInfo(domain, username);

    if (bResult) {
      memcpy(gtalk_entropy, static_key, sizeof(static_key));

      long M = 2147483647; // modulus
      long A = 48271;      // multiplier
      long Q = M / A;
      long R = M % A;
      long seed = 387822687;   // this could be wrong but does work
                               // and is exactly 9 digits

      seed = A * (seed % Q) - R * (seed / Q);
      seed += M;

      long idx = 0;

      // mix with username
      for (std::wstring::size_type i = 0;i < username.length();i++) {
        gtalk_entropy[idx++ % 4] ^= username[i] * seed;
        seed *= A;

      // mix with domain
      for (std::wstring::size_type i = 0;i < domain.length();i++) {
        gtalk_entropy[idx++ % 4] ^= domain[i] * seed;
        seed *= A;
    return bResult;


Once the entropy is initialized, it’s possible to decode the pw value into binary which is just a DPAPI blob.

Instead of the usual Base64 encoding, Google Talk uses Base16 with a custom alphabet and a multiplier value.

// convert base16 string into binary
void gtalk_decode(BYTE blob[], std::wstring input) {
    std::wstring alphabet = L"!"#$%&'()*+,-./0";
    long seed = gtalk_entropy[0] | 1;
    long A = 69621;
    PBYTE p = blob;

    for (size_t i = 4;i < input.length();i += 2) {
      int c;

      c  = (alphabet.find_first_of( + 0))) << 4;
      c |= (alphabet.find_first_of( + 1))) & 0x0f;

      *p++ = c - (seed & 0xff);
      seed *= A;

Finally, CryptUnprotectData() is used to decrypt the UNICODE password.

// decrypt blob using DPAPI
BOOL gtalk_decrypt(BYTE password[], BYTE blob_data[], size_t blob_size) {
    DATA_BLOB DataIn, DataEntropy, DataOut;

    DataEntropy.cbData = sizeof(gtalk_entropy);
    DataEntropy.pbData = (BYTE*)gtalk_entropy;

    DataIn.cbData = blob_size;
    DataIn.pbData = blob_data;

    BOOL bResult = CryptUnprotectData(&DataIn, NULL, &DataEntropy, NULL, NULL, 1, &DataOut);

    if (bResult) {
      memcpy(password, DataOut.pbData, DataOut.cbData);
      password[DataOut.cbData] = 0;
    return bResult;


The password is protected reasonably well although the entropy generation is a little redundant and I dare say a classic example of Security through Obscurity :)
CryptProtectData and Base64 would provide the same level of protection.

2 thoughts on “Password Algorithms: Google Talk

  1. Great summary of the GTalk password decoding and decryption process! If you are considering a series of these posts, I think taking a look at how AIM does does Blowfish encryption on their stored passwords could be interesting; the information is out there but very difficult to piece together (at least for me!).

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>