Code:
/*
Copyright 2005,2006,2007,2008,2009 Luigi Auriemma
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
http://www.gnu.org/licenses/gpl-2.0.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/stat.h>
#include <openssl/aes.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#ifdef WIN32
#include <windows.h>
#else
#endif
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
#define VER "0.2.3c"
#define FINDUSER "_Users" "\x01\x50"
#define FINDPASS "Phrase" "\x01\x50"
#define NOKEY "NoMachineSpecificPassphraseAvailable"
#ifdef WIN32
u8 *get_file(u8 *folder);
int regkey(HKEY hKey, LPCTSTR lpSubKey, LPTSTR lpValueName, u8 *buff, DWORD len);
#endif
void get_key(u8 *keystr);
int hex2byte(u8 *input, u8 *output);
void AESPHM_GenerateIvFromSeed(u8 *ivSeed, u8 *iv);
int AESPHM_Decrypt(u8 *passphrase, u8 *output, u8 *input);
void steampwd(u8 *fname, u8 *keystr);
u8 *fd_read(u8 *name, int *fdlen);
u8 *find_data(u8 *buff, int buffsz, u8 *str);
void wait_exit(int ret);
void std_err(void);
int main(int argc, char *argv[]) {
#ifdef WIN32
u8 steampath[MAX_PATH + 1];
#endif
u8 keystr[1000],
*fname = NULL,
*p;
setbuf(stdout, NULL);
fputs("\n"
"Steam password decoder "VER"\n"
"\n", stderr);
fprintf(stderr, "\n"
"Usage: %s [ClientRegistry.blob/password] [key]\n"
"\n", argv[0]);
if(argc < 2) {
#ifdef WIN32
if(GetWindowLong(GetForegroundWindow(), GWL_WNDPROC)) {
regkey(
HKEY_CURRENT_USER,
"Software\\Valve\\Steam", "SteamPath",
steampath, sizeof(steampath));
if(steampath[0]) {
for(p = steampath; *p; p++) {
if(*p == '/') *p = '\\';
}
}
fname = get_file(steampath);
} else
#endif
{
fprintf(stderr,
"Error: please specify the location of the blob file or directly the encrypted\n"
" password\n");
wait_exit(1);
}
} else {
fname = argv[1];
}
if(argc > 2) {
sprintf(keystr, "%.*s", sizeof(keystr), argv[2]);
} else {
get_key(keystr);
}
steampwd(fname, keystr);
fprintf(stderr, "\n- done\n");
wait_exit(0);
return(0);
}
#ifdef WIN32
u8 *get_file(u8 *folder) {
OPENFILENAME ofn;
static u8 filename[MAX_PATH + 1];
static const u8 filter[] =
"BLOB file\0" "*.blob\0"
"(*.*)\0" "*.*\0"
"\0" "\0";
filename[0] = 0;
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFilter = filter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = filename;
ofn.nMaxFile = sizeof(filename);
ofn.lpstrTitle = "Select ClientRegistry.blob";
ofn.lpstrInitialDir = folder;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_LONGNAMES | OFN_EXPLORER;
if(!GetOpenFileName(&ofn)) wait_exit(1);
return(filename);
}
int regkey(HKEY hKey, LPCTSTR lpSubKey, LPTSTR lpValueName, u8 *buff, DWORD len) {
HKEY key;
DWORD type;
buff[0] = 0;
if(RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) {
return(-1);
}
if(RegQueryValueEx(key, lpValueName, NULL, &type, buff, &len) != ERROR_SUCCESS) {
RegCloseKey(key);
return(-1);
}
RegCloseKey(key);
if(type != REG_SZ) return(-1);
return(0);
}
#endif
void get_key(u8 *keystr) {
#ifdef WIN32
u8 ProductId[256], // 24
MachineGuid[256], // 37
io[256]; // 16
if(regkey(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "ProductId",
ProductId, sizeof(ProductId)) < 0) ProductId[0] = 0;
fprintf(stderr, "ProductId %s\n", ProductId);
if(regkey(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Cryptography", "MachineGuid",
MachineGuid, sizeof(MachineGuid)) < 0) MachineGuid[0] = 0;
fprintf(stderr, "MachineGuid %s\n", MachineGuid);
if(regkey(
HKEY_CURRENT_USER,
"Software\\Valve\\Half-Life\\Settings", "io",
io, sizeof(io)) < 0) io[0] = 0;
fprintf(stderr, "HalfLife io %s\n", io);
sprintf(keystr, "%s%s%s", ProductId, MachineGuid, io);
if(!keystr[0]) strcpy(keystr, NOKEY);
#else
fprintf(stderr, "\nError: you must provide a valid decryption key (this is not Windows!)\n");
wait_exit(1);
#endif
}
int hex2byte(u8 *input, u8 *output) {
static const u8 hextable[256] =
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\x00\x00\x00\x00\x00"
"\x00\x0a\x0b\x0c\x0d\x0e\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x0a\x0b\x0c\x0d\x0e\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
u8 *i,
*o;
for(i = input, o = output; i[0] && i[1]; i += 2, o++) {
*o = (hextable[i[0]] << 4) | hextable[i[1]];
}
return(o - output);
}
enum {
MINIMUM_CIPHERTEXT_LENGTH = 46,
IV_SEED_SIZE = 8,
IV_SIZE = 16,
HMAC_SHA_DIGESTSIZE = 20
};
void AESPHM_GenerateIvFromSeed(u8 *ivSeed, u8 *iv) {
int i;
u8 ivHash[SHA256_DIGEST_LENGTH];
SHA256(ivSeed, IV_SEED_SIZE, ivHash);
for(i = 0; i < 16; i++) {
iv[i] = ivHash[i] ^ ivHash[i + 16];
}
}
void myaesdec(AES_KEY *ctx, int length, int *ivoff, u8 *iv, u8 *input, u8 *output) {
int c,
n;
for(n = *ivoff; length--; n = (n + 1) & 15) {
if(!n) AES_ecb_encrypt(iv, iv, ctx, AES_ENCRYPT);
c = *input++;
*output++ = (unsigned char)(c ^ iv[n]);
iv[n] = (unsigned char)c;
}
*ivoff = n;
}
// thanx to an anonymous: all the encryption of Steam was just a simple "AESPHM_Decrypt(key, password, password);"
int AESPHM_Decrypt(u8 *passphrase, u8 *output, u8 *input) {
AES_KEY ctx;
int inputlen,
ivoff,
paddingLen,
payloadLen;
u8 key[SHA256_DIGEST_LENGTH],
iv[IV_SIZE],
checkDigest[HMAC_SHA_DIGESTSIZE],
firstPadByte,
*digest,
*ivSeed,
*payload;
input = strdup(input);
inputlen = hex2byte(input, input);
if(inputlen < MINIMUM_CIPHERTEXT_LENGTH) {
fprintf(stderr, "\nError: the encrypted hex password (%d) is shorter than %d bytes\n", inputlen, MINIMUM_CIPHERTEXT_LENGTH);
free(input);
return(-1);
}
digest = input + inputlen - HMAC_SHA_DIGESTSIZE;
ivSeed = digest - IV_SEED_SIZE;
SHA256(passphrase, strlen(passphrase), key);
HMAC(EVP_sha1(), key, SHA256_DIGEST_LENGTH, input, digest - input, checkDigest, NULL);
if(memcmp(checkDigest, digest, HMAC_SHA_DIGESTSIZE)) {
fprintf(stderr, "\nError: the hmac checksum differs, the key is wrong\n");
free(input);
return(-1);
}
AESPHM_GenerateIvFromSeed(ivSeed, iv);
AES_set_encrypt_key(key, SHA256_DIGEST_LENGTH << 3, &ctx);
ivoff = 0; // AES_cfb128_encrypt in OpenSSL, but due to a difference with Crypto++ I use myaesdec
myaesdec(&ctx, 1, &ivoff, iv, input, &firstPadByte);
paddingLen = (firstPadByte & 15) + 3;
if((input + paddingLen) > ivSeed) {
fprintf(stderr, "\nError: the initial padded bytes (%d) are longer than the seed (%d)\n", paddingLen, IV_SEED_SIZE);
free(input);
return(-1);
}
payload = input + paddingLen;
payloadLen = ivSeed - payload;
if(payloadLen) {
myaesdec(&ctx, paddingLen - 1, &ivoff, iv, input + 1, output);
myaesdec(&ctx, payloadLen, &ivoff, iv, payload, output);
}
free(input);
return(payloadLen);
}
void steampwd(u8 *fname, u8 *keystr) {
int len,
fdsize,
pwds;
u16 nlen;
u8 *fdbuff,
*fdnext,
//*user,
*p;
fprintf(stderr, "- decryption key:\n \"%s\"\n\n", keystr);
fdbuff = fd_read(fname, &fdsize);
if(!fdbuff) {
fprintf(stderr, "- the input will be considered a password\n");
len = strlen(fname);
fdsize = 64 + len;
fdbuff = malloc(fdsize);
p = fdbuff;
p += sprintf(p, "%-30s", FINDPASS);
*(u16 *)p = 0; p += 2;
*(u32 *)p = len; p += 4 + 0;
strcpy(p, fname);
}
printf("\n");
fdnext = fdbuff;
for(pwds = 0;; pwds++) { // totally useless, steam saves only the password of the last logged user
/* disabled because doesn't support multi users and I have no interest in them
user = find_data(fdnext, fdsize, FINDUSER);
if(user) {
user += 36; // the blob file format is not so hard but I want the fastest solution
len = *(u16 *)user; user += 2 + 4;
printf(" USERNAME: %.*s\n", len, user);
}*/
p = find_data(fdnext, fdsize, FINDPASS);
if(!p) {
if(pwds) break;
fprintf(stderr, "\n"
"- password not found, probably you have not saved it or have disabled the\n"
" storing of the local account informations through the Settings menu of Steam\n");
wait_exit(1);
}
p += 30; // the blob file format is not so hard but I want the fastest solution
nlen = *(u16 *)p; p += 2;
len = *(u32 *)p; p += 4 + nlen;
fdsize -= (p - fdnext);
fdnext = p;
if((len > 0) && (len < fdsize)) {
p[len] = 0;
len = AESPHM_Decrypt(keystr, p, p); // try the retrieved key
if((len < 0) && strcmp(keystr, NOKEY)) {
fprintf(stderr, "- now I try with the decryption key \"%s\"\n", NOKEY);
len = AESPHM_Decrypt(NOKEY, p, p); // if the first fails try with NOKEY
}
if(len >= 0) {
printf(" PASSWORD: %.*s\n", len, p);
}
}
}
free(fdbuff);
}
u8 *fd_read(u8 *name, int *fdlen) {
struct stat xstat;
FILE *fd;
u8 *buff;
fprintf(stderr, "- open file %s\n", name);
fd = fopen(name, "rb");
if(!fd) return(NULL);
fstat(fileno(fd), &xstat);
buff = malloc(xstat.st_size);
if(!buff) std_err();
fread(buff, 1, xstat.st_size, fd);
fclose(fd);
*fdlen = xstat.st_size;
return(buff);
}
u8 *find_data(u8 *buff, int buffsz, u8 *str) {
int strsz;
u8 *p,
*limit;
strsz = strlen(str);
limit = buff + buffsz - strsz;
for(p = buff; p <= limit; p++) {
if(!memcmp(p, str, strsz)) return(p);
}
return(NULL);
}
void wait_exit(int ret) {
#ifdef WIN32
if(GetWindowLong(GetForegroundWindow(), GWL_WNDPROC)) {
fprintf(stderr, "\n Press RETURN to exit\n");
fgetc(stdin);
}
#endif
exit(ret);
}
void std_err(void) {
perror("\nError");
wait_exit(1);
}