I have combined Luigi's ClientRegistry.blob parsing function(s) with VALVe's exported SteamDecryptDataForThisMachine() function. This is just an example of how to combine the two since I have not seen anyone else post such yet besides Luigi's original steampwd v0.2.1 which uses its own decryption function. I also tried to insert comments where applicable to better explain what his code is doing step by step.
Note that '\x50\x68\x72\x61\x73\x65\x01\x50" represents "Phrase.P", I just thought it more simple than using a macro.
Enjoy.
gcc -s -O2 DecryptStoredSteamPassword.c
Code:
//
// DecryptStoredSteamPassword.c
//
// Combines Luigi's ClientRegistry.blob parsing function(s)
// with VALVe's exported SteamDecryptDataForThisMachine() function
//
// small code changes and comments added by desxor
#include <stdio.h>
#include <stdint.h>
#include <sys/stat.h>
#include <windows.h>
void SteamParseEncryptedPassPhrase(uint8_t *fname);
uint8_t *find_data(uint8_t *buff, int buffsz, uint8_t *str);
typedef int (__cdecl *SteamDecryptDataForThisMachine_t)(char *a, int b, char *c, int d, int *e);
SteamDecryptDataForThisMachine_t SteamDecryptDataForThisMachine;
int main(int argc, char *argv[]) {
HANDLE hSteamDLL;
// this path should be grabbed automatically from registry "InstallPath" value, but since
// this is just an example, desxor is being lazy.
hSteamDLL = LoadLibrary("C:\\Program Files\\Steam\\STEAM.DLL");
if(!hSteamDLL) {
printf("\nError: the file STEAM.DLL has not been found.\n");
exit(1);
}
SteamDecryptDataForThisMachine = (void *)GetProcAddress(hSteamDLL, "SteamDecryptDataForThisMachine");
if(!SteamDecryptDataForThisMachine) {
printf("\nError: the function SteamDecryptDataForThisMachine has not been found.\n");
exit(1);
}
// this path should be grabbed automatically from registry "InstallPath" value as well
SteamParseEncryptedPassPhrase("C:\\Program Files\\Steam\\ClientRegistry.blob");
FreeLibrary(hSteamDLL);
return(0);
}
void SteamParseEncryptedPassPhrase(uint8_t *fname) {
int len, fdsize, pwds;
uint16_t nlen;
uint8_t *fdbuff, *fdnext, *p, *buff;
char decpass[100];
struct stat xstat;
FILE *fd;
fd = fopen(fname, "rb");
if(!fd) {
printf("Could not open %s for reading/memory search.\n", fname);
exit(1);
} else {
fstat(fileno(fd), &xstat);
fdbuff = malloc(xstat.st_size);
if(!fdbuff) {
printf("Could not allocate file into memory.\n");
// comments for the following are pretty much the same as below so look there for
// a more detailed description of what's going on
len = strlen(fname);
fdsize = 64 + len;
fdbuff = malloc(fdsize);
p = fdbuff;
p += sprintf(p, "%-30s", "\x50\x68\x72\x61\x73\x65\x01\x50");
*(uint16_t *)p = 0;
p += 2;
*(uint32_t *)p = len;
p += 4 + 0;
strcpy(p, fname);
fclose(fd);
goto next;
}
// load our file into memory for searching and reading its data
fread(fdbuff, 1, xstat.st_size, fd);
fclose(fd);
fdsize = xstat.st_size;
}
next:
fdnext = fdbuff;
for(pwds = 0;; pwds++) {
// search for unique phrase text using memcmp(), using a great little function provided by Luigi A.
// his function will search the entire allocated memory for the data you specify and return with it
// if its found, otherwise null
p = find_data(fdnext, fdsize, "\x50\x68\x72\x61\x73\x65\x01\x50");
if(!p) {
if(pwds) break;
printf("An encrypted and stored password could not be located, exiting.\n");
exit(1);
}
// skip 30 bytes past the PHRASE text to the encrypted password until we come
// to "04 00" or simply 4, (0x04 + (0x00 * 256)), we now have 24 bytes remaining
p += 30;
// this is the 16 bit number we are looking for, save it in the following format:
// num = byte1 + (byte2 * 256)
nlen = *(uint16_t *)p;
// skip the next 2 bytes of the 16bit number we just saved
p += 2;
// after we skipped 2 bytes, we come to 32bit number (4 bytes) which should always be the
// size of our encrypted string, it should appear as "5c 00 00 00", which equals 0x5c or simply, 92
// save this in the same format as before
len = *(uint32_t *)p;
// now we skip the 4 bytes that we _just read_ PLUS the amount of bytes specified by the first
// 16 bit number we just saved, should be 2 bytes anyhow
// this should now bring us to our encrypted password located in ClientRegistry.blob which should be
// 92 (0x5c) characters long
p += 4 + nlen;
// an example of the data, provided by Luigi:
// 50 68 72 61 73 65 01 50 7e 00 00 00 00 00 00 00 Phrase.P~.......
// 04 00 04 00 00 00 01 00 00 00 02 00 00 00 04 00 ................
// 5c 00 00 00 02 00 00 00 39 41 46 41 42 44 39 36 \.......9AFABD96
// 32 30 43 45 43 34 39 31 46 38 33 44 43 45 31 32 20CEC491F83DCE12
// 36 33 33 44 39 43 44 41 41 44 45 30 42 36 46 46 633D9CDAADE0B6FF
// 41 32 42 42 45 30 31 32 45 38 39 32 37 33 36 39 A2BBE012E8927369
// 35 32 35 37 43 44 43 45 39 35 37 32 41 37 30 38 5257CDCE9572A708
// 38 42 32 43 41 43 30 33 37 44 43 38 33 33 36 33 8B2CAC037DC83363
// 33 33 35 35 12 00 2a 00 00 00 43 6c 6f 63 3355..*...Cloc
fdsize -= (p - fdnext);
fdnext = p;
// as long as our length is greater than zero but no bigger than fdsize, execute the exported decryption function
if((len > 0) && (len < fdsize)) {
// null terminate the end of our string, otherwise it will cause problems
p[len] = 0;
printf("Found stored encrypted password:\n \"%s\"\n\n", p);
if(!SteamDecryptDataForThisMachine(p, strlen(p), decpass, sizeof(decpass), &len)) {
printf("Password: %.*s\n", len, decpass);
} else {
printf("Unable to decrypt the stored password, is this the same machine it was encrypted on?\n");
}
}
}
free(fdbuff);
}
uint8_t *find_data(uint8_t *buff, int buffsz, uint8_t *str) {
int strsz;
uint8_t *limit;
strsz = strlen(str);
limit = buff + buffsz - strsz;
for(; buff <= limit; buff++) {
if(!memcmp(buff, str, strsz)) return(buff);
}
return(NULL);
}