Luigi Auriemma

aluigi.org (ARCHIVE-ONLY FORUM!)
It is currently 19 Jul 2012 18:15

All times are UTC [ DST ]





Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 34 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: How Steampwd works
PostPosted: 18 Aug 2007 09:56 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
usually many people ask me how steampwd works, so I think this thread will be enough useful.

My steampwd tool does the most simple job in the world, it first finds the encoded password in the ClientRegistry.blob file (or you can pass this password directly at command-line) and then calls a function in steam.dll

steam.dll is the dll used by Steam for doing many things included the encryption and decryption of the account password stored in the ClientRegistry.blob file.
The dll you find in the steampwd package is just the original one I found in a Steam version of a couple of years ago and nothing has been changed in it... it's just the original one!
Is not possible for my tool to easily use newer steam.dll files because the function I call is not exported and so is first necessary to find it and in any case the dll in the steampwd package is the one which gives less problems.

Anyway if you need to find where is located this function in newer steam.dll you can use a small trick, open steam.dll with a disassembler and search in what function is used the following string:

\p4clients\rel_beta\Projects\Common\Misc\EncryptDecryptWithMachineDependentPassphrase.cpp

the offset you need to replace is just the one you see at the beginning
of the function (in steampwd you must use the file offset address not the RVA one, I did it for better compatibility).

If you need info about how exactly the decryption works I can't help too much because I have never finished the reversing of the algorithm, I know only that the key is the concatenation of the following registry values:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProductId
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid
HKEY_CURRENT_USER\Software\Valve\Half-Life\Settings\io

that's why you can decrypt the passwords only on the system where they were encrypted!

Now return to how steampwd works internally.
before calling the needed steam.dll function we need another step, the creation of the following structure in memory:

struct {
uint32_t boh1; // 0
uint8_t *pass; // password
uint32_t boh2; // 0
uint32_t boh3; // 0
uint32_t boh4; // 0
uint32_t passlen; // length of the password
uint32_t boh5; // length of the binary encoded password?
uint8_t boh6[40]; // needed with newer steam.dll files
} steam_pwd;

it's an output structure which will be filled by steam.dll and will contain the decrypted password.
All you need to do is just placing zeroes in all its fields, that's all.
If you don't know how to create a similar structure in your programming language simply create buffer of about 68 bytes, the decrypted password will be pointed at the position 4 of this buffer.

Then call the function in steam.dll:

steam_dll_pwd(pwdenc, pwdenc_length, &steam_pwd);

where pwdenc is just the encoded password, pwdenc_length is its length and &steam_pwd is the pointer to the previous structure.

If everything will work correctly nothing will crash and the clear decrypted string will be available in the pass field of the structure.


Top
 Profile  
 
 
 Post subject:
PostPosted: 18 Aug 2007 12:36 

Joined: 14 Aug 2007 13:32
Posts: 71
Wow thank's for the share luigi that was interesting..


Top
 Profile  
 
 Post subject:
PostPosted: 26 Dec 2007 17:49 

Joined: 26 Dec 2007 17:41
Posts: 1
Hello.

I have installed a dll dissasambler and found the link.

But stil I dont know what to change to what.

"in steampwd you must use the file offset address not the RVA one"

What is file offset adress?


Top
 Profile  
 
 Post subject:
PostPosted: 26 Dec 2007 19:41 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
in steampwd.c you must modify the value near "#define STEAMDLL_OFFSET".
That offset you see in the disassembler is the RVA offset, in short remove 0x20000000 to that value and you will have the one to insert in STEAMDLL_OFFSET


Top
 Profile  
 
 Post subject:
PostPosted: 15 Jan 2008 02:33 

Joined: 15 Jan 2008 02:30
Posts: 4
what disassembler do you use luigi? I have tried many and none can search for that type of string.


Top
 Profile  
 
 Post subject:
PostPosted: 15 Jan 2008 02:58 

Joined: 15 Jan 2008 02:30
Posts: 4
I found SUB_L30038FA0:
but 0x38FA0 dosen't work, just says password: (null). it dosen't crash though


Top
 Profile  
 
 Post subject:
PostPosted: 15 Jan 2008 10:32 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
Uhmmm seems that the recent steam.dll has changed the base address but the offset you specify is very strange.
in the steam.dll updated now in this moment the decryption function seems the one at offset 0x301E7F90 so try using 0x1E7F90 in steampwd.c and it "should" work


Top
 Profile  
 
 Post subject:
PostPosted: 15 Jan 2008 10:39 

Joined: 15 Jan 2008 02:30
Posts: 4
Yeah I just found that one in my latest search. Nope that crashes it :/ hmm.. do you mind taking the time to figure out the new steam.dll? I would love you for it :D lol.


Top
 Profile  
 
 Post subject:
PostPosted: 15 Jan 2008 16:41 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
consider that steampwd uses a structure which was good with the old steam.dll file so although sometimes is possible to use the newer versions of the file it's also true that they could not work.

I have tried with the new steam.dll and crashes here too.
Why don't you use the older steam.dll file?


Top
 Profile  
 
 Post subject:
PostPosted: 15 Jan 2008 16:57 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
Ok, take steampwd.c and:

1)
substituite boh6[40] with *boh6

2)
before calling steam_dll_pwd() add:

steam_pwd.pass = malloc(100);
steam_pwd.boh6 = malloc(40); // works also with 0 or ""

at least it works although I doubt about that it's very "compatible"


Top
 Profile  
 
 Post subject:
PostPosted: 20 Jan 2008 21:31 

Joined: 20 Jan 2008 21:25
Posts: 2
I have downloaded the steampwd.exe file u have made, but im not sure how to use it. Should i just put it in my steam folder and run it? in that case where is the output so i can see my password for steam? I have also replaced the steam.dll file in the steam folder with the steam.dll file from ur steampwd.rar, but nothing change.. plz help since steam support is not very friendly!


Top
 Profile  
 
 Post subject:
PostPosted: 21 Jan 2008 20:13 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
Just unzip steampwd.exe and steam.dll in a new folder and then drag the file Clientregistry.blob (you can find it in the Steam folder) over the steampwd executable and it will do the job.


Top
 Profile  
 
 Post subject:
PostPosted: 21 Jan 2008 21:55 

Joined: 20 Jan 2008 21:25
Posts: 2
ahh, it was the "drag" part i missed :P thx alot, now i have my password again :D


Top
 Profile  
 
 Post subject:
PostPosted: 24 Feb 2008 09:44 

Joined: 24 Feb 2008 08:31
Posts: 10
Figured out password algorithm. Requirements:
- Crypto++ (Tested with 5.5.2)
- AES PHM (http://www.denisbider.com/aesphm.zip)

Code:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "aesphm.h"

using namespace CryptoPP;

// WARNING: EXTREMELY SENSITIVE INFORMATION!
#define ACCOUNT_PHRASE_SIZE (XXX + 8 + 20)
static byte accountphrase[ACCOUNT_PHRASE_SIZE] = {
   <Contents of "phrase" for user>
   // Cipher Text (18 bytes+)
   // IV Seed (8 bytes)
   // Digest (20 bytes)
};

byte *GetRegistryValue(HKEY hKey, const char *key, const char *value, size_t &size)
{
   LONG status;
   HKEY hSubKey;
   byte *result = NULL;

   char *tempKey = _strdup(key);
   char *subSubKey = strchr(tempKey, '\\');
   if (subSubKey)
      *(subSubKey++) = '\0';

   if((status = RegOpenKeyExA(hKey, tempKey, 0, KEY_WOW64_64KEY | KEY_QUERY_VALUE, &hSubKey)) == ERROR_SUCCESS)
   {
      if (subSubKey && *subSubKey)
         result = GetRegistryValue(hSubKey, subSubKey, value, size);
      else
      {
         // Get the size of the value.
         DWORD valueSize = 0;
         status = RegQueryValueExA(hSubKey, value, NULL, NULL, NULL, &valueSize);
         if (status == ERROR_SUCCESS)
         {
            BYTE *valueData = new BYTE[valueSize];
            memset(valueData, 0x00, valueSize);

            status = RegQueryValueExA(hSubKey, value, NULL, NULL, valueData, &valueSize);
            if (status == ERROR_SUCCESS)
            {
               result = valueData;
               size = (size_t)valueSize;
            }
            else
            {
               delete[] valueData;
            }
         }
      }
   }
   else
   {
      printf("Could not open key \"%s\"\r\n", tempKey);
   }

   delete[] tempKey;

   return result;
}

void AppendData(byte *&data, size_t &size, const byte *input, size_t inputSize)
{
   size_t tempSize;
   byte *temp;
   if (data == NULL)
   {
      tempSize = inputSize;
      temp = new byte[tempSize];
      memcpy(temp, input, tempSize);
   }
   else
   {
      tempSize = size + inputSize;
      temp = new byte[tempSize];
      memcpy(temp, data, size);
      memcpy(temp + size, input, inputSize);
      delete[] data;
   }

   data = temp;
   size = tempSize;
}

char *GetPassPhraseText()
{
   size_t size = 0;
   byte *data = NULL;

   size_t productIdSize = 0;
   byte *productId = GetRegistryValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "ProductId", productIdSize);
   AppendData(data, size, productId, productIdSize - 1);
   delete[] productId;

   size_t machineGuidSize = 0;
   byte *machineGuid = GetRegistryValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", "MachineGuid", machineGuidSize);
   AppendData(data, size, machineGuid, machineGuidSize - 1);
   delete[] machineGuid;

   size_t ioSize = 0;
   byte *io = GetRegistryValue(HKEY_CURRENT_USER, "Software\\Valve\\Half-Life\\Settings", "io", ioSize);
   AppendData(data, size, io, ioSize - 1);
   delete[] io;

   const byte nullByte[1] = { 0x00 };
   AppendData(data, size, nullByte, 1);

   return (char *)data;
}

void PrintHex(const byte *data, size_t datasize)
{
   for (size_t i = 0; i < datasize; i++)
   {
      if (i != 0)
         printf(" ");
      printf("%02X", data[i]);
   }
}

int main(int argc, char *argv[])
{
   char *passphraseText = GetPassPhraseText();
   printf("Passphrase: %s\r\n", passphraseText);

   // Inputs
   ConstByteArrayParameter passphrase(const_cast<const char *>(passphraseText));
   ConstByteArrayParameter input(accountphrase, ACCOUNT_PHRASE_SIZE);
   // Output
   size_t outputsize = ACCOUNT_PHRASE_SIZE - 8 - 20;
   byte *output = new byte[outputsize];
   memset(output, 0x00, outputsize);

   // Processing
   DecodingResult result = AESPHM::Decrypt(passphrase, output, input);

   // Display
   if (result.isValidCoding)
   {
      char *password = new char[result.messageLength + 1];
      memcpy(password, output, result.messageLength);
      password[result.messageLength] = '\0';

      printf("Password: %s\r\n", password);
      printf("Password Hex: (%u bytes)\r\n", result.messageLength);
      PrintHex(output, result.messageLength);
      printf("\r\n");

      delete[] password;
   }
   else
   {
      printf("Password decryption failed.\r\n");
   }

   delete[] passphraseText;
   delete[] output;

   return 0;
}

This was released purely for academic purposes. I do not condone using this for writing viruses/spyware/etc. It's probably that Valve will change their algorithm now that it's been figured out...

EDIT: Made some changes to the code. It can now get the registry values automatically.


Last edited by AnonymousCoward on 25 Feb 2008 04:06, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: 24 Feb 2008 16:32 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
I was searching the AESPHM file from long time.
Anyway well done.

I have implemented the AESPHM::Decrypt function in C and works fine except for some bytes which result different (for example if the password is abcdefg I decrypt it as abcde..), so now I'm checking what strange operations are performed by Crypto++ to give these results... seems a problem of padding or something similar in this library.

How much long was the password you tested?
Because the AES ecb encryption used the second time (16 bytes at time) gives different results and now I'm checking if it's something related to Crypto++ (uhmm I highly doubt) or a tiny modification made by Valve or something else.


Top
 Profile  
 
 Post subject:
PostPosted: 25 Feb 2008 04:16 

Joined: 24 Feb 2008 08:31
Posts: 10
I have tested this with an 8 digit password. It works just fine. I also modified the above code to automatically retrieve the registry values and concatenate them (messy implementation though). It has also been modified to only show the bytes within the message length. As well as showing when the decryption failed.

There does appear to be an extra byte for my password on the 9th byte (outside of the message length), but other than that the password is exactly the same.

Note that the implementation is using CFB, not ECB (so of course the results will differ, ECB effectively does nothing with the IV).

I highly doubt there's a bug in Crypto++'s Rijndael implementation, as it's been FIPS-140-2 validated.

EDIT: I figured out why there is an extra byte at the end of mine. It's because of the following lines in AESPHM:
Code:
// Decrypt random padding (less 1 byte) to output.
decryptor.ProcessString(output, input.begin()+1, paddingLen-1);
// Decrypt actual data to output.
decryptor.ProcessString(output, payload, payloadLen);

What it does it is decrypts the "random padding" to the output, then it overwrites the random padding with the real data to the output. So the character I was talking about was just the leftovers of the random padding.

So an 8 character password has 10 bytes of padding ((15 - (8 % 15)) + 3). So it writes 9 bytes (because there's 1 byte it decrypts separately to figure out how much padding there is) to the output buffer, then it only overwrites 8 of those bytes. Which should result in 1 byte of excess padding written to the output.


Top
 Profile  
 
 Post subject:
PostPosted: 25 Feb 2008 10:55 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
AnonymousCoward wrote:
I have tested this with an 8 digit password. It works just fine.

the problem is that the ECB encryption is applied to block of 16 bytes and as you have seen the AESPHM block is composed by one byte which tells how much padding bytes are available, the padding bytes and finally the data.
So the 8 digit password could still be within the first 16 bytes-block, naturally I'm considering the hypotesis that the Crypto++ implementation is not bugged for explaining the problem.

Anyway now I try to compile your C++ example for watching if the problem I have with my password exists also with Crypto++.

Quote:
Note that the implementation is using CFB, not ECB (so of course the results will differ, ECB effectively does nothing with the IV).

CFB is just ECB plus the xoring of the data with the iv

Quote:
So an 8 character password has 10 bytes of padding ((15 - (8 % 15)) + 3). So it writes 9 bytes (because there's 1 byte it decrypts separately to figure out how much padding there is) to the output buffer, then it only overwrites 8 of those bytes.

18 bytes like the same size of my block... in this case if you can correctly decrypt your password with Crypto++ and the latest bytes are corrects probably means there is a bug in the implementation.
Naturally I have tested both Xyssl AES and the cfb128 of Openssl and the strange thing is that where Crypto++ talks about "decryption" it's considered "encryption" in these other implementations... this is really really strange

I will do other tests and I will keep you update


Top
 Profile  
 
 Post subject:
PostPosted: 25 Feb 2008 11:22 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
Result of the test: Crypto++ decrypts the password correctly.
So the bug is in this library when it starts to do the second pass of ECB from the second AES block... I don't know what to say except blah


Top
 Profile  
 
 Post subject:
PostPosted: 25 Feb 2008 12:25 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
And the following are more details:
in short Crypto++ in CFB decryption mode does the XORing of data and iv and then places the old data in iv.
This one is the classical operation made in cfb when you select AES_DECRYPT (so correct) BUT with the difference that it uses the encryption mode for all the rest... what a chaos anyway the most importan this is that I have the code working now.

What's your name Anonymous Coward?


Top
 Profile  
 
 Post subject:
PostPosted: 25 Feb 2008 13:23 

Joined: 24 Feb 2008 08:31
Posts: 10
The reason it fails in the other libraries (I believe) is that the data is not correctly padded to the block length of AES. Crypto++ allows messages that are "incomplete", while it appears that these other implementations do not allow this, so they just writes null bytes. (When I tried this in C#, it was able to decrypt the first block, but failed on the second block with the following message: "Padding is invalid and cannot be removed." this is after I added a bunch of dummy bytes to the end to get it to even initialize.)

I would rather keep my name anonymous on this forum publicly, but I'll PM you with my real handle.


Top
 Profile  
 
 Post subject:
PostPosted: 28 Feb 2008 11:35 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
A couple of days ago I have released the new version of Steampwd.
Thanx again AnonymousCoward


Top
 Profile  
 
 Post subject: Compiled source != inzip exe
PostPosted: 20 Aug 2008 01:42 

Joined: 20 Aug 2008 01:29
Posts: 3
I compiled your source but it doesn't work while the inzip exe works.
How do you compile it ?

Cortex


Top
 Profile  
 
 Post subject:
PostPosted: 20 Aug 2008 10:42 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
if you refer to steampwddll forget it because there are problems with the direct usage of the dll so could or couldn't work sometimes (I have already spent some time on it and gave up).

if you refer to the latest version of steampwd, another user reported a similar problem but at the moment I have not found the cause of this differences, probably the assembly in padlock.c.

do you use a native 64bit OS with a 64bit compiler?
in this case the problem can be explained by the usage of "unsigned long" in the source code of the AES/SHA*/padlock components (which have not written by me)


Top
 Profile  
 
 Post subject:
PostPosted: 20 Aug 2008 19:20 

Joined: 20 Aug 2008 01:29
Posts: 3
Yes, sorry, I talked about steampwddll.
I tryed to use it because I compiled steampwd on WinXP 64 with gcc 32: it works on a 32 platform but not on my 64 one, I'll try to follow the right topic.

Thank you,

Cortex


Top
 Profile  
 
 Post subject:
PostPosted: 21 Aug 2008 22:54 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
the following is the updated version of steampwddll using the method suggested by Tughack and works perfectly


Attachments:
steampwddll.zip [2.71 KiB]
Downloaded 449 times
Top
 Profile  
 
 Post subject:
PostPosted: 22 Aug 2008 00:17 

Joined: 20 Aug 2008 01:29
Posts: 3
It seems to be working nice

Good job ;-) Thank you

Cortex


Top
 Profile  
 
 Post subject: Re: How Steampwd works
PostPosted: 05 Sep 2008 14:37 

Joined: 11 Aug 2008 20:49
Posts: 1
Aluigi, I don't know how you got the 2 last params since I didnt share them with you. As you know, I didnt like the idea of this method going public. You probably got them from nodereality.com or from someone who got them from there. Anyway, credits for finding the encrypted password go to you, credits for this method go to me (Tughack) and credits for the last 2 params (it wouldn't work well without them) go to Alan.

You can find the post about the params finding here:

https://nodereality.com/viewtopic.rar?id=356

Btw, its __cdecl, not __stdcall.

Hope this clarifies things.

Tughack


Top
 Profile  
 
 Post subject: Re: How Steampwd works
PostPosted: 05 Sep 2008 15:38 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
this is the first time I hear about that website you have posted
I have found the missing last 2 parameters in about 5 minutes after our last chat of almost 3 weeks ago since finally of I had some free time to focus on this thing.

and no, I have never asked to be credited for this thing in fact the "by Luigi Auriemma" on the top of the C code is there only for specifying that I have written that source code and there are no references at runtime (even the title) just because it's only an example code which is located only on this forum where is located also all the rest of the discussion

Anyway I repeat what I told you in chat, it's only the calling of a function exported by a public DLL so I don't understand all this mistery around this thing... so sincerely I don't understand why you are angry


Top
 Profile  
 
 Post subject: Re: How Steampwd works
PostPosted: 05 Sep 2008 23:08 

Joined: 01 Sep 2008 07:40
Posts: 31
Tughack wrote:
Aluigi, I don't know how you got the 2 last params since I didnt share them with you.

man you really are an amateur , no offense, but you sound like an idiot spouting off like that. that function and its params have been public and on many websites for along time now. not to mention the leaked hl2 source from 2003.


Top
 Profile  
 
 Post subject: Re: How Steampwd works
PostPosted: 05 Sep 2008 23:59 

Joined: 13 Aug 2007 21:44
Posts: 4068
Location: http://aluigi.org
desxor I need to correct you on some things.

The SteamDecryptDataForThisMachine function created by Valve has been exported in steam.dll in some recent versions, in fact when I started the steampwd project in the far 2005 it didn't existed... unfortunately for me

the hl2 source code doesn't contain info about this or other Steam related functions or AESPHM_Decrypt because this is part of the Steam program and not of the game.

for the rest I have already talked with Tughack and naturally he was wrong.
was enough to check the dates of my post and the one on nodereality.com... I'm not so good to travel in the future and returning 10 days back in time ih ih ih

the only thing that I don't like is having someone which says/accuses that I have "copied" a sizeof(pwd) and a &len from some places in the future that even I don't know.
with all the stuff I have reversed in my life I expect at least a bit more of estimation, not much but at least enough to avoid something so ridiculous as this thing of the 2 parameters moreover considering that Tughack contacted me in PM asking me to do this and I found all the 5 parameters (in two different occasions, I was completely uninterested to this thing since my steampwd is already complete)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 34 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for: