/** * Calculates the CD-Key hash for use in SID_AUTH_CHECK (0x51) * Returns the length of the generated hash; call getHash and pass * it a character array that is at least this size. Returns 0 on failure. * * Note that clientToken and serverToken will be added to the buffer and * hashed as-is, regardless of system endianness. It is assumed that * the program's extraction of the server token does not change its * endianness, and since the client token is generated by the client, * endianness is not a factor. */ size_t CDKeyDecoder::calculateHash(uint32_t clientToken, uint32_t serverToken) { struct CDKEYHASH kh; SHA1Context sha; if (!initialized || !keyOK) return 0; hashLen = 0; kh.clientToken = clientToken; kh.serverToken = serverToken; switch (keyType) { case KEY_STARCRAFT: case KEY_WARCRAFT2: kh.product = (uint32_t) LSB4(product); kh.value1 = (uint32_t) LSB4(value1); kh.value2.s.zero = 0; kh.value2.s.v = (uint32_t) LSB4(value2); keyHash = new char[20]; calcHashBuf((char*) &kh, 24, keyHash); hashLen = 20; #if DEBUG bncsutil_debug_message_a("%s: Hash calculated.", cdkey); bncsutil_debug_dump(keyHash, 20); #endif return 20; case KEY_WARCRAFT3: kh.product = (uint32_t) MSB4(product); kh.value1 = (uint32_t) MSB4(value1); memcpy(kh.value2.l.v, w3value2, 10); if (SHA1Reset(&sha)) return 0; if (SHA1Input(&sha, (const unsigned char*) &kh, 26)) return 0; keyHash = new char[20]; if (SHA1Result(&sha, (unsigned char*) keyHash)) { SHA1Reset(&sha); return 0; } SHA1Reset(&sha); hashLen = 20; #if DEBUG bncsutil_debug_message_a("%s: Hash calculated.", cdkey); bncsutil_debug_dump(keyHash, 20); #endif return 20; default: return 0; } }
/** * Single-hashes the password for account creation and password changes. * * outBuffer MUST be at least 20 bytes long. */ MEXP(void) hashPassword(const char* password, char* outBuffer) { calcHashBuf(password, std::strlen(password), outBuffer); #if DEBUG bncsutil_debug_message_a("hashPassword(\"%s\") =", password); bncsutil_debug_dump(outBuffer, 20); #endif }
MEXP(long) get_mpq_seed(int mpq_number) { if (((size_t) mpq_number) >= checkrevision_seeds.size()) { bncsutil_debug_message_a("error: no known revision check seed for " "MPQ#%u", mpq_number); return 0; } return checkrevision_seeds[mpq_number]; }
/** * Double-hashes the given password using the given * server and client tokens. * * outBuffer MUST be at least 20 bytes long. */ MEXP(void) doubleHashPassword(const char* password, uint32_t clientToken, uint32_t serverToken, char* outBuffer) { char intermediate[28]; uint32_t* lp; calcHashBuf(password, std::strlen(password), intermediate + 8); lp = (uint32_t*) &intermediate; lp[0] = clientToken; lp[1] = serverToken; calcHashBuf(intermediate, 28, outBuffer); #if DEBUG bncsutil_debug_message_a("doubleHashPassword(\"%s\", 0x%08X, 0x%08X) =", password, clientToken, serverToken); bncsutil_debug_dump(outBuffer, 20); #endif }
int CDKeyDecoder::processStarCraftKey() { int accum, pos, i; char temp; int hashKey = 0x13AC9741; char cdkey[14]; std::strcpy(cdkey, this->cdkey); // Verification accum = 3; for (i = 0; i < (int) (keyLen - 1); i++) { accum += ((tolower(cdkey[i]) - '0') ^ (accum * 2)); } if ((accum % 10) != (cdkey[12] - '0')) { bncsutil_debug_message_a("error: %s is not a valid StarCraft key", cdkey); return 0; } // Shuffling pos = 0x0B; for (i = 0xC2; i >= 7; i -= 0x11) { temp = cdkey[pos]; cdkey[pos] = cdkey[i % 0x0C]; cdkey[i % 0x0C] = temp; pos--; } // Final Value for (i = (int) (keyLen - 2); i >= 0; i--) { temp = toupper(cdkey[i]); cdkey[i] = temp; if (temp <= '7') { cdkey[i] ^= (char) (hashKey & 7); hashKey >>= 3; } else if (temp < 'A') {
MEXP(int) getExeInfo(const char* file_name, char* exe_info, size_t exe_info_size, uint32_t* version, int platform) { const char* base = (char*) 0; unsigned long file_size; FILE* f = (FILE*) 0; int ret; #ifdef MOS_WINDOWS HANDLE hFile; FILETIME ft; SYSTEMTIME st; LPBYTE buf; VS_FIXEDFILEINFO* ffi; DWORD infoSize, bytesRead; #else cm_pe_t pe; cm_pe_resdir_t* root; cm_pe_resdir_t* dir; cm_pe_version_t ffi; size_t i; struct stat st; struct tm* time; #endif if (!file_name || !exe_info || !exe_info_size || !version) return 0; base = basename(file_name); switch (platform) { case BNCSUTIL_PLATFORM_X86: #ifdef MOS_WINDOWS infoSize = GetFileVersionInfoSize(file_name, &bytesRead); if (infoSize == 0) return 0; buf = (LPBYTE) VirtualAlloc(NULL, infoSize, MEM_COMMIT, PAGE_READWRITE); if (buf == NULL) return 0; if (GetFileVersionInfo(file_name, NULL, infoSize, buf) == FALSE) return 0; if (!VerQueryValue(buf, "\\", (LPVOID*) &ffi, (PUINT) &infoSize)) return 0; *version = ((HIWORD(ffi->dwProductVersionMS) & 0xFF) << 24) | ((LOWORD(ffi->dwProductVersionMS) & 0xFF) << 16) | ((HIWORD(ffi->dwProductVersionLS) & 0xFF) << 8) | (LOWORD(ffi->dwProductVersionLS) & 0xFF); #if DEBUG bncsutil_debug_message_a("%s version = %d.%d.%d.%d (0x%08X)", base, (HIWORD(ffi->dwProductVersionMS) & 0xFF), (LOWORD(ffi->dwProductVersionMS) & 0xFF), (HIWORD(ffi->dwProductVersionLS) & 0xFF), (LOWORD(ffi->dwProductVersionLS) & 0xFF), *version); #endif VirtualFree(buf, 0lu, MEM_RELEASE); #else pe = cm_pe_load(file_name); if (!pe) return 0; root = cm_pe_load_resources(pe); if (!root) { cm_pe_unload(pe); return 0; } for (i = 0; i < root->subdir_count; i++) { dir = (root->subdirs + i); if (dir->name == 16) { if (!cm_pe_fixed_version(pe, dir->subdirs->resources, &ffi)) { cm_pe_unload_resources(root); cm_pe_unload(pe); return 0; } break; } } *version = ((HIWORD(ffi.dwProductVersionMS) & 0xFF) << 24) | ((LOWORD(ffi.dwProductVersionMS) & 0xFF) << 16) | ((HIWORD(ffi.dwProductVersionLS) & 0xFF) << 8) | (LOWORD(ffi.dwProductVersionLS) & 0xFF); #if DEBUG bncsutil_debug_message_a("%s version = %d.%d.%d.%d (0x%08X)", base, (HIWORD(ffi.dwProductVersionMS) & 0xFF), (LOWORD(ffi.dwProductVersionMS) & 0xFF), (HIWORD(ffi.dwProductVersionLS) & 0xFF), (LOWORD(ffi.dwProductVersionLS) & 0xFF), *version); #endif cm_pe_unload_resources(root); cm_pe_unload(pe); #endif break; case BNCSUTIL_PLATFORM_MAC: case BNCSUTIL_PLATFORM_OSX: f = fopen(file_name, "r"); if (!f) return 0; if (fseek(f, -4, SEEK_END) != 0) { fclose(f); return 0; } if (fread(version, 4, 1, f) != 1) { fclose(f); return 0; } #ifdef MOS_WINDOWS fclose(f); #endif } #ifdef MOS_WINDOWS hFile = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; file_size = GetFileSize(hFile, NULL); if (!GetFileTime(hFile, &ft, NULL, NULL)) { CloseHandle(hFile); return 0; } if (!FileTimeToSystemTime(&ft, &st)) { CloseHandle(hFile); return 0; } CloseHandle(hFile); ret = snprintf(exe_info, exe_info_size, "%s %02u/%02u/%02u %02u:%02u:%02u %lu", base, st.wMonth, st.wDay, (st.wYear % 100), st.wHour, st.wMinute, st.wSecond, file_size); #else if (!f) f = fopen(file_name, "r"); if (!f) return 0; if (fseek(f, 0, SEEK_END) == -1) { fclose(f); return 0; } file_size = ftell(f); fclose(f); if (stat(file_name, &st) != 0) return 0; time = gmtime(&st.st_mtime); if (!time) return 0; switch (platform) { case BNCSUTIL_PLATFORM_MAC: case BNCSUTIL_PLATFORM_OSX: if (time->tm_year >= 100) // y2k time->tm_year -= 100; break; } ret = (int) snprintf(exe_info, exe_info_size, "%s %02u/%02u/%02u %02u:%02u:%02u %lu", base, (time->tm_mon+1), time->tm_mday, time->tm_year, time->tm_hour, time->tm_min, time->tm_sec, file_size); #endif #if DEBUG bncsutil_debug_message(exe_info); #endif return ret; }
MEXP(int) checkRevision(const char* formula, const char* files[], int numFiles, int mpqNumber, unsigned long* checksum) { long values[4], ovd[4], ovs1[4], ovs2[4]; char ops[4]; const char* token; int curFormula = 0; file_t f; uint8_t* file_buffer; uint32_t* dwBuf; uint32_t* current; size_t seed_count; #if DEBUG int i; bncsutil_debug_message_a("checkRevision(\"%s\", {", formula); for (i = 0; i < numFiles; i++) { bncsutil_debug_message_a("\t\"%s\",", files[i]); } bncsutil_debug_message_a("}, %d, %d, %p);", numFiles, mpqNumber, checksum); #endif if (!formula || !files || numFiles == 0 || mpqNumber <= 0 || !checksum) { bncsutil_debug_message("error: checkRevision() parameter sanity check " "failed"); return 0; } seed_count = checkrevision_seeds.size(); if (seed_count == 0) { initialize_checkrevision_seeds(); seed_count = checkrevision_seeds.size(); } if (seed_count <= (size_t) mpqNumber) { bncsutil_debug_message_a("error: no revision check seed value defined " "for MPQ number %d", mpqNumber); return 0; } token = formula; while (token && *token) { if (*(token + 1) == '=') { int variable = BUCR_GETNUM(*token); if (variable < 0 || variable > 3) { bncsutil_debug_message_a("error: Unknown revision check formula" " variable %c", *token); return 0; } token += 2; // skip over equals sign if (BUCR_ISNUM(*token)) { values[variable] = atol(token); } else { if (curFormula > 3) { // more than 4 operations? bloody hell. bncsutil_debug_message("error: Revision check formula" " contains more than 4 operations; unsupported."); return 0; } ovd[curFormula] = variable; ovs1[curFormula] = BUCR_GETNUM(*token); ops[curFormula] = *(token + 1); ovs2[curFormula] = BUCR_GETNUM(*(token + 2)); curFormula++; } } for (; *token != 0; token++) { if (*token == ' ') { token++; break; } } } // Actual hashing (yay!) // "hash A by the hashcode" values[0] ^= checkrevision_seeds[mpqNumber]; for (int i = 0; i < numFiles; i++) { size_t file_len, remainder, rounded_size, buffer_size; f = file_open(files[i], FILE_READ); if (!f) { bncsutil_debug_message_a("error: Failed to open file %s", files[i]); return 0; } file_len = file_size(f); remainder = file_len % 1024; rounded_size = file_len - remainder; file_buffer = (uint8_t*) file_map(f, file_len, 0); if (!file_buffer) { file_close(f); bncsutil_debug_message_a("error: Failed to map file %s into memory", files[i]); return 0; } if (remainder == 0) { // Mapped buffer may be used directly, without padding. dwBuf = (uint32_t*) file_buffer; buffer_size = file_len; } else { // Must be padded to nearest KB. size_t extra = 1024 - remainder; uint8_t pad = (uint8_t) 0xFF; uint8_t* pad_dest; buffer_size = file_len + extra; dwBuf = (uint32_t*) malloc(buffer_size); if (!dwBuf) { bncsutil_debug_message_a("error: Failed to allocate %d bytes " "of memory as a temporary buffer", buffer_size); file_unmap(f, file_buffer); file_close(f); return 0; } memcpy(dwBuf, file_buffer, file_len); file_unmap(f, file_buffer); file_buffer = (uint8_t*) 0; pad_dest = ((uint8_t*) dwBuf) + file_len; for (size_t j = file_len; j < buffer_size; j++) { *pad_dest++ = pad--; } } current = dwBuf; for (size_t j = 0; j < buffer_size; j += 4) { values[3] = LSB4(*(current++)); for (int k = 0; k < curFormula; k++) { switch (ops[k]) { case '+': values[ovd[k]] = values[ovs1[k]] + values[ovs2[k]]; break; case '-': values[ovd[k]] = values[ovs1[k]] - values[ovs2[k]]; break; case '^': values[ovd[k]] = values[ovs1[k]] ^ values[ovs2[k]]; break; case '*': // well, you never know values[ovd[k]] = values[ovs1[k]] * values[ovs2[k]]; break; case '/': // well, you never know values[ovd[k]] = values[ovs1[k]] / values[ovs2[k]]; break; default: // unrecognized operation // shit file_unmap(f, dwBuf); file_close(f); return 0; } } } if (file_buffer) file_unmap(f, file_buffer); else if (dwBuf && file_buffer == 0) free(dwBuf); // padded buffer file_close(f); } *checksum = (unsigned long) LSB4(values[2]); #if DEBUG bncsutil_debug_message_a("\tChecksum = %lu", *checksum); #endif return 1; }
/** * Creates a new CD-key decoder object, using the specified key. * keyLength should be the length of the key, NOT INCLUDING the * null-terminator. Applications should use isKeyValid after using * this constructor to check the validity of the provided key. */ CDKeyDecoder::CDKeyDecoder(const char* cdKey, size_t keyLength) { unsigned int i; initialized = 0; product = 0; value1 = 0; value2 = 0; keyOK = 0; hashLen = 0; cdkey = (char*) 0; w3value2 = (char*) 0; keyHash = (char*) 0; if (keyLength <= 0) return; // Initial sanity check if (keyLength == 13) { // StarCraft key for (i = 0; i < keyLength; i++) { if (!isdigit(cdKey[i])) return; } keyType = KEY_STARCRAFT; #if DEBUG bncsutil_debug_message_a( "Created CD key decoder with STAR key %s.", cdKey ); #endif } else { // D2/W2/W3 key for (i = 0; i < keyLength; i++) { if (!isalnum(cdKey[i])) return; } switch (keyLength) { case 16: keyType = KEY_WARCRAFT2; #if DEBUG bncsutil_debug_message_a( "Created CD key decoder with W2/D2 key %s.", cdKey ); #endif break; case 26: keyType = KEY_WARCRAFT3; #if DEBUG bncsutil_debug_message_a( "Created CD key decoder with WAR3 key %s.", cdKey ); #endif break; default: #if DEBUG bncsutil_debug_message_a( "Created CD key decoder with unrecognized key %s.", cdKey ); #endif return; } } cdkey = new char[keyLength + 1]; initialized = 1; keyLen = keyLength; strcpy(cdkey, cdKey); switch (keyType) { case KEY_STARCRAFT: keyOK = processStarCraftKey(); #if DEBUG bncsutil_debug_message_a("%s: ok=%d; product=%d; public=%d; " "private=%d", cdkey, keyOK, getProduct(), getVal1(), getVal2()); #endif break; case KEY_WARCRAFT2: keyOK = processWarCraft2Key(); #if DEBUG bncsutil_debug_message_a("%s: ok=%d; product=%d; public=%d; " "private=%d", cdkey, keyOK, getProduct(), getVal1(), getVal2()); #endif break; case KEY_WARCRAFT3: keyOK = processWarCraft3Key(); #if DEBUG bncsutil_debug_message_a("%s: ok=%d; product=%d; public=%d; ", cdkey, keyOK, getProduct(), getVal1()); #endif break; default: return; } }