004C2700: call @Controls@TControl@GetText$qqrv ; Controls::TControl::GetText(void) EIP-> mov edx, [ebp+var_8] mov eax, [ebp+var_4] call sub_4C23A8 test al, al jnz short loc_4C277C ... mov edx, offset _str_WKeyError.Text call sub_4AF2F8 ... jmp short loc_4C27D2 loc_4C277C: ... mov edx, offset _str_WRegistrationTh.Text call sub_4AF2F8
check_key_4C23A8 proc near ... mov [ebp+key_8], edx mov [ebp+this_4], eax ... mov [ebp+is_right_key_9], 0 cmp [ebp+key_8], 0 jz loc_4C257B lea edx, [ebp+key_copy_28] mov eax, [ebp+key_8] call copy_digits_492734 mov edx, [ebp+key_copy_28] mov eax, ds:pp_key_4F305C call @System@@LStrAsg$qqrv ; System::__linkproc__ LStrAsg(void) ... mov eax, ds:pp_dirname_4F2F54 push dword ptr [eax] push offset _str_slash.Text push offset _str_CharmSolitaire.Text push offset _str__udf.Text lea eax, [ebp+udf_filename_2C] mov edx, 4 call str_cat_40522C mov edx, [ebp+udf_filename_2C] ; %GAME_DIR%\CharmSolitaire.udf mov eax, [ebp+mem_stream_encrypted_10] call @Classes@TMemoryStream@LoadFromFile$qqrx17System@AnsiString_0 ; Classes::TMemoryStream::LoadFromFile(System::AnsiString) mov eax, ds:pp_key_4F305C cmp dword ptr [eax], 0 jz short loc_4C2496 mov eax, ds:pp_key_4F305C mov eax, [eax] call strlen_40516C 004C2473: cmp eax, 18h jnz short loc_4C2496 004C2478: ... loc_4C25A5: mov al, [ebp+is_right_key_9] ... retn check_key_4C23A8 endp
004C2478: lea edx, [ebp+var_30] mov eax, ds:pp_key_4F305C mov eax, [eax] call sub_4924D0 mov edx, [ebp+var_30] mov eax, ds:off_4F2C24 call @System@@LStrAsg$qqrv ; System::__linkproc__ LStrAsg(void) jmp short loc_4C24A5
key_to_hex_4924D0 proc near ... mov [ebp+p_res_10], edx mov [ebp+key_C], eax ... lea edx, [ebp+key_copy_24] mov eax, [ebp+key_C] call copy_digits_492734 mov eax, [ebp+key_copy_24] lea edx, [ebp+mixed_str_14] call mix_symbols_492688 lea eax, [ebp+mixed_str_14] mov ecx, 3 mov edx, 1 call delete_symbols_40540C jmp short loc_492539 loc_492527: lea eax, [ebp+mixed_str_14] mov ecx, 1 mov edx, 1 call delete_symbols_40540C loc_492539: mov eax, [ebp+mixed_str_14] cmp byte ptr [eax], 30h ; '0' jz short loc_492527 ; delete leading zeros push 0 ; default value push 0 ; default value mov eax, [ebp+mixed_str_14] call @Sysutils@StrToInt64Def$qqrx17System@AnsiStringj ; Sysutils::StrToInt64Def(System::AnsiString,__int64) mov [ebp+v64lo_8], eax mov [ebp+v64hi_4], edx mov eax, [ebp+p_res_10] call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &) mov [ebp+i_18], 1 loc_492562: mov eax, [ebp+i_18] test byte ptr [ebp+eax-1+v64lo_8], 7Fh jbe short loc_49258C lea eax, [ebp+char_str_28] mov edx, [ebp+i_18] mov dl, byte ptr [ebp+edx-1+v64lo_8] and dl, 7Fh call str_from_pchar_405084 ; Borland Visual Component Library & Packages mov edx, [ebp+char_str_28] mov eax, [ebp+p_res_10] call @System@@LStrCat$qqrv ; System::__linkproc__ LStrCat(void) mov eax, [ebp+p_res_10] loc_49258C: inc [ebp+i_18] cmp [ebp+i_18], 9 jnz short loc_492562 loc_4925A2: ... retn key_to_hex_4924D0 endp
mixed_str_14 = mix_symbols_492688(key); v64_4 = StrToInt64Def(mixed_str_14, 0); while (v64_4[i] & 0x7F > 0) (string)p_res_10 += (char)v64_4[i] & 0x7F, i++;
0x0CCCCCCCCCCCCCCC = 922337203685477580 000000922337203685477580 0 0 0 2 3 7 0 6 5 7 5 0 0 0 0 9 2 3 2 3 8 4 7 8 000237065750000923238478
004C2478: lea edx, [ebp+p_key64_30] mov eax, ds:pp_key_4F305C mov eax, [eax] call key_to_hex_4924D0 mov edx, [ebp+p_key64_30] mov eax, ds:p_key_bytes_4F2C24 call @System@@LStrAsg$qqrv ; System::__linkproc__ LStrAsg(void) jmp short loc_4C24A5 ... loc_4C24A5: mov ecx, ds:p_key_bytes_4F2C24 mov ecx, [ecx] mov edx, [ebp+mem_stream_decrypted_14] mov eax, [ebp+mem_stream_encrypted_10] call sub_492B48 mov eax, [ebp+mem_stream_decrypted_14] call sub_492C94 test al, al jz loc_4C255F ... loc_4C255F: mov [ebp+is_right_key_9], 0 ... loc_4C25A5: mov al, [ebp+is_right_key_9] ... ret check_key_4C23A8 endp
decrypt_492B48 proc near ... mov [ebp+key_bytes_28], ecx mov [ebp+mem_stream_decrypted_24], edx mov [ebp+mem_stream_encrypted_20], eax ... mov eax, [ebp+key_bytes_28] call strlen_40516C test eax, eax jnz short loc_492B87 ... loc_492B87: ... ; key_bytes_28 8 ... ; key_bytes8_34 8 key_bytes_28 ... ; , loc_492BD4: lea edx, [ebp+buf_14] mov ecx, 8 mov eax, [ebp+mem_stream_encrypted_20] mov ebx, [eax] call dword ptr [ebx+0Ch] ; read bytes mov [ebp+bytes_read_C], eax xor eax, eax mov [ebp+i_2C], eax loc_492BEC: mov eax, [ebp+i_2C] mov al, [ebp+eax+key_bytes8_34] mov edx, [ebp+i_2C] xor byte ptr [ebp+edx+buf_14], al inc [ebp+i_2C] cmp [ebp+i_2C], 8 jnz short loc_492BEC cmp [ebp+bytes_read_C], 8 jnz short loc_492C3C push ebp call sub_492A6C pop ecx xor eax, eax mov [ebp+i_2C], eax loc_492C15: mov eax, [ebp+i_2C] mov al, [ebp+eax+key_bytes8_34] mov edx, [ebp+i_2C] xor byte ptr [ebp+edx+decrypted_buf_1C], al inc [ebp+i_2C] cmp [ebp+i_2C], 8 jnz short loc_492C15 lea edx, [ebp+decrypted_buf_1C] mov ecx, [ebp+bytes_read_C] mov eax, [ebp+mem_stream_decrypted_24] mov ebx, [eax] call dword ptr [ebx+10h] ; write bytes jmp short loc_492C4A loc_492C3C: lea edx, [ebp+buf_14] mov ecx, [ebp+bytes_read_C] mov eax, [ebp+mem_stream_decrypted_24] mov ebx, [eax] call dword ptr [ebx+10h] ; write bytes loc_492C4A: cmp [ebp+bytes_read_C], 8 jz short loc_492BD4 ... retn decrypt_492B48 endp
bytes_read = mem_stream_encrypted->read(buf, 8); for (i = 0; i < 8; i++) buf ^= key[i]; if (bytes_read == 8) { sub_492A6C(buf, out decrypted_buf); for (i = 0; i < 8; i++) decrypted_buf ^= key[i]; mem_stream_decrypted->write(decrypted_buf, bytes_read); } else { mem_stream_decrypted->write(buf, bytes_read); }
( ) 11111111 10000000 00000000 10000000 00000000 10000000 00000000 -> 10000000 00000000 10000000 00000000 10000000 00000000 10000000 00000000 10000000
C[x][y] ^ K[x][y] ^ K[y][x] = D[y][x] C[y][x] ^ K[y][x] ^ K[x][y] = D[x][y] C - , D - , K - , x y - .
T[x][y] = T[y][x] C[x][y] ^ T[x][y] = D[y][x] C[y][x] ^ T[x][y] = D[x][y]
#define FIXED_0 0 #define FIXED_1 1 #define UNKNOWN 2 const unsigned char bitTypes[8][8] = { {0, 0, 0, 0, 0, 0, 0, 0}, {2, 0, 0, 0, 0, 0, 0, 0}, {2, 2, 0, 0, 0, 0, 0, 0}, {2, 2, 2, 0, 0, 0, 0, 0}, {2, 2, 2, 2, 0, 0, 0, 0}, {2, 2, 2, 2, 2, 0, 0, 0}, {2, 2, 2, 2, 2, 2, 0, 0}, {2, 2, 2, 2, 2, 2, 2, 0} };
The current value of the brute force counter is distributed over the UNKNOWN bits (there is a code at the end of the article).
C[x][y] ^ K[x][y] ^ K[y][x] ^ C[y][x] ^ K[y][x] ^ K[x][y] = D[y][x] ^ D[x][y] C[x][y] ^ C[y][x] ^ (K[x][y] ^ K[x][y]) ^ (K[y][x] ^ K[y][x]) = D[y][x] ^ D[x][y] C[x][y] ^ C[y][x] = D[y][x] ^ D[x][y]
#define FIXED_0 0 #define FIXED_1 1 #define UNKNOWN 2 const unsigned char bitTypes[8][8] = { {0, 0, 0, 0, 0, 0, 0, 0}, {2, 0, 0, 0, 0, 0, 0, 0}, {2, 2, 0, 0, 0, 0, 0, 0}, {2, 2, 2, 0, 0, 0, 0, 0}, {2, 2, 2, 2, 0, 0, 0, 0}, {2, 2, 2, 2, 2, 0, 0, 0}, {2, 2, 2, 2, 2, 2, 0, 0}, {1, 1, 0, 0, 1, 1, 0, 0} };
</ Level> 0x0D, 0x0A
#include <vcl.h> #pragma hdrstop #include "MainUnit.h" #include <vector> //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TMainForm *MainForm; //--------------------------------------------------------------------------- __fastcall TMainForm::TMainForm(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- inline unsigned int getBit(unsigned char byte, unsigned int bitNumber) { return (byte & ((unsigned char)1 << bitNumber) ? 1 : 0); } inline void setBit(unsigned char &byte, unsigned int bitNumber, unsigned int bitValue) { if (bitValue) byte |= ((unsigned char)1 << bitNumber); else byte &= ~((unsigned char)1 << bitNumber); } int isText(unsigned char *pData, int streamSize) { int isText = 1; if (streamSize < 1) return isText; char prevChar = 0; do { if (*pData < 0x20 && *pData != 9) { if ((*pData == 0x0D || *pData == 0x0A)) { // // 0x0A, //if (*pData == 0x0A && prevChar != 0x0D) //{ // isText = 0; // break; //} } else { isText = 0; break; } } prevChar = *pData; pData++; } while (--streamSize); return isText; } #define FIXED_0 0 #define FIXED_1 1 #define UNKNOWN 2 const unsigned char bitTypes[8][8] = { // {0, 0, 0, 0, 0, 0, 0, 0}, {2, 0, 0, 0, 0, 0, 0, 0}, {2, 2, 0, 0, 0, 0, 0, 0}, {2, 2, 2, 0, 0, 0, 0, 0}, {2, 2, 2, 2, 0, 0, 0, 0}, {2, 2, 2, 2, 2, 0, 0, 0}, {2, 2, 2, 2, 2, 2, 0, 0}, {1, 1, 0, 0, 1, 1, 0, 0} }; void getKeyMatrix(unsigned int keyBits, unsigned char matrix[8]) { int x, y; unsigned int bitValue = 0, bitNumber = 0; memset(matrix, 8, 0); for(y = 0; y < 8; y++) { for(x = 0; x < 8; x++) { // if (bitTypes[y][x] == UNKNOWN) { bitValue = getBit(((unsigned char*)&keyBits)[bitNumber / 8], bitNumber % 8); bitNumber++; } else if (bitTypes[y][x] == FIXED_1) bitValue = 1; else bitValue = 0; setBit(matrix[y], x, bitValue); } } } void reverseMatrix(unsigned char block[8]) { unsigned int x, y, bitValue; unsigned char tmpBlock[8]; for (y = 0; y < 8; y++) { for (x = 0; x < 8; x++) { bitValue = getBit(block[x], y); setBit(tmpBlock[y], x, bitValue); } } memcpy(block, tmpBlock, 8); } void decryptBlock(unsigned int keyBits, unsigned char *encryptedBlock, unsigned char *decryptedBlock, unsigned int blockSize) { unsigned char key[8]; unsigned int i; getKeyMatrix(keyBits, key); for(i = 0; i < blockSize; i++) decryptedBlock[i] = encryptedBlock[i] ^ key[i]; if (blockSize == 8) { reverseMatrix(decryptedBlock); for(i = 0; i < 8; i++) decryptedBlock[i] = decryptedBlock[i] ^ key[i]; } } void decryptText(unsigned char *encryptedText, unsigned char *decryptedText, unsigned int textSize, unsigned int keyBits) { unsigned int position = 0, blockSize = 8, bytesToRead = 0; unsigned int i, j; while(position < textSize) { if (position + blockSize <= textSize) bytesToRead = blockSize; else bytesToRead = textSize - position; decryptBlock(keyBits, encryptedText + position, decryptedText + position, bytesToRead); // if (bytesToRead == 8 && !isText(decryptedText + position, 8)) break; position += bytesToRead; } } void getKeyVariants(unsigned char *encryptedText, unsigned int textSize, std::vector<unsigned int> &keyList) { unsigned int variantsCount = 0; unsigned int possibleBits = 0; unsigned char *decryptedText = new unsigned char[textSize]; //for (possibleBits = 0; possibleBits < (1 << 20); possibleBits++) for (possibleBits = 0; possibleBits < (1 << 6); possibleBits++) { decryptText(encryptedText, decryptedText, textSize, possibleBits); if (isText(decryptedText, textSize - textSize % 8)) { keyList.push_back(possibleBits); variantsCount++; } } variantsCount = variantsCount; // delete []decryptedText; } AnsiString getKeyText(unsigned char keyMatrix[8]) { AnsiString str = "000000000000000000000000" + IntToStr(*(__int64*)keyMatrix); str = str.SubString(str.Length() - 24 + 1, 24); // 1 AnsiString keyText = ""; for(int i = 0; i < 24; i += 2) keyText.cat_printf("%c", str.c_str()[i + 1]); for(int i = 0; i < 24; i += 2) keyText.cat_printf("%c", str.c_str()[i]); return keyText; } //--------------------------------------------------------------------------- std::vector<unsigned int> keyList; void __fastcall TMainForm::btnStartClick(TObject *Sender) { AnsiString filename = "F:\\Games\\Charm Solitaire\\CharmSolitaire.udf"; TMemoryStream *encryptedStream = new TMemoryStream(); encryptedStream->LoadFromFile(filename); getKeyVariants((unsigned char *)encryptedStream->Memory, encryptedStream->Size, keyList); unsigned char keyMatrix[8]; getKeyMatrix(keyList[0], keyMatrix); getKeyText(keyMatrix); } void __fastcall TMainForm::btnDecryptClick(TObject *Sender) { AnsiString filename = "F:\\Games\\Charm Solitaire\\CharmSolitaire.udf"; TMemoryStream *encryptedStream = new TMemoryStream(); encryptedStream->LoadFromFile(filename); unsigned int keyBits = keyList[0]; unsigned int textSize = encryptedStream->Size; unsigned char *decryptedText = new unsigned char[textSize + 1]; decryptedText[textSize] = 0; decryptText((unsigned char *)encryptedStream->Memory, decryptedText, textSize, keyBits); mDecryptedText->Text = AnsiString((char*)decryptedText); } //---------------------------------------------------------------------------
Source: https://habr.com/ru/post/260071/