#include #include #include #ifdef _WIN32 # include # include # include #else # include # include # include # include "reader/tchar.h" #endif #include //#define DEBUG_OUT 1 #define BLOCK_LENGTH 4096 #define ENCRKEY_OFFSET 7 #define ENCRKEY_LEN 32 #define MACKEY_OFFSET 41 #define MACKEY_LEN 4 #define PUBKEY_OFFSET 98 #define PUBKEY_LEN 64 #define SV_OFFSET 164 #define SV_LEN 8 #define IV_LEN 8 #define ENCPARAMSET_LEN 13 static BYTE encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static BYTE *decoding_table = NULL; static int mod_table[] = {0, 2, 1}; void build_decoding_table(); BYTE *base64_decode(const BYTE *data, DWORD input_length, DWORD *output_length); static void CleanUp(void); static void HandleError(char *s); static HCRYPTPROV hProv = 0; // Дескриптор CSP static HCRYPTKEY hKey = 0; // Дескриптор закрытого ключа static HCRYPTKEY hPubKey = 0; // Дескриптор открытого ключа ФСС static HCRYPTKEY hSessionKey = 0; // Дескриптор сессионного ключа static HCRYPTKEY hAgreeKey = 0; // Дескриптор ключа согласования static FILE *fhEncrypt=NULL; // Зашифрованный файл static FILE *fhOutput=NULL; // Расшифрованный файл #ifdef DEBUG_OUT static FILE *fhCertB64=NULL; // Сертификат ФСС из xml в base64 static FILE *fhCert=NULL; // Сертификат ФСС из xml бинарный static FILE *fhEncodingParametersB64; // Параметры шифрования из xml в base64 static FILE *fhEncodingParameters; // Параметры шифрования из xml в бинарном виде static FILE *fhEncodedContentB64; // Содержимое из xml в base64 static FILE *fhEncodedContent; // Содержимое из xml которое надо расшифровать в бинарном виде с вектором инициализации static FILE *fhMacKey; static FILE *fhKeyBlobSimple; static FILE *fhKeyBlob; static FILE *fhContent; // чистое содержимое которое надо расшифровать без вектора инициализации #endif char SearchStr1[] = ""; char SearchStr2[] = ""; char SearchStr3[] = ""; char SearchStr4[] = ""; #define MAX_PUBLICKEYBLOB_SIZE 200 int main(int argc, char *argv[]) { BYTE * pbEncrypt; // Указатель на содержимое зашифрованного файла DWORD cbEncrypt = 0; // Длина содержимого BYTE * pbClrContent; // Указатель на содержимое зашифрованного файла, после очистки от спецсимволов и пробелов DWORD cbClrContent = 0; // Длина содержимого после очистики BYTE * pbContent; DWORD cbContent = 0; BYTE * pbCert; // Указатель на сертификат ФСС DWORD cbCert = 0; // длина блока памяти BYTE pbKeyBlob[MAX_PUBLICKEYBLOB_SIZE]; // Указатель на ключевой BLOB DWORD dwBlobLen = MAX_PUBLICKEYBLOB_SIZE; // Длина ключевого BLOBа BYTE pPublicKeyBlob[PUBKEY_LEN]; DWORD cbPublicKeyBlob; BYTE *pbKeyBlobSimple = NULL; // Указатель на сессионный ключевой BLOB DWORD cbBlobLenSimple; // Длина сессионного ключевого BLOBа BYTE pbIV[IV_LEN]; // Вектор инициализации сессионного ключа DWORD dwIV = IV_LEN; // Длина вектора инициализации DWORD keyParam = CRYPT_MODE_CBC; DWORD paddingMode = ISO10126_PADDING; //cpm_ISO10126 ALG_ID ke_alg = CALG_PRO_EXPORT /*CALG_PRO12_EXPORT*/; // алгоритм ключа согласования CRYPT_SIMPLEBLOB_HEADER tSimpleBlobHeaderStandart; //неменяемая часть блоба BYTE pbEncryptionParamSetStandart[ENCPARAMSET_LEN] = {0x30, 0x0B, 6, 9, 0x2A, 0x85, 3, 7, 1, 2, 5, 1, 1 }; // OBJECT IDENTIFIER 1.2.643.7.1.2.5.1.1 tc26CipherZ (TC26 params Z for GOST 28147-89) DWORD cbEncryptionParamSetStandart = ENCPARAMSET_LEN; //длина неменяемой части блоба PCCERT_CONTEXT pCertContext = NULL; DWORD dwBytesRead; DWORD i, pos_Cert1, pos_Cert2, pos_EncP1, pos_EncP2, pos_Enc1, pos_Enc2; BYTE * pbBufferB64; DWORD cbBufferB64; tSimpleBlobHeaderStandart.BlobHeader.aiKeyAlg = CALG_G28147; tSimpleBlobHeaderStandart.BlobHeader.bType = SIMPLEBLOB; tSimpleBlobHeaderStandart.BlobHeader.bVersion = BLOB_VERSION; tSimpleBlobHeaderStandart.BlobHeader.reserved = 0; tSimpleBlobHeaderStandart.EncryptKeyAlgId = CALG_G28147; tSimpleBlobHeaderStandart.Magic = G28147_MAGIC; if(argc < 4) { printf("Decryption tool for FSS ELN service.\nver 0.01, IbZ(c) Studio (tm) :)\n" ); printf("\n\tusing: %s \n\n", argv[0]); HandleError( "Not enough input parameters given" ); } // Открытие файла, в котором хранится зашифрованный ответ FSS. if(!(fhEncrypt = fopen(argv[2], "r+b" ))) HandleError( "Problem opening the file with encrypted xml\n" ); #ifdef DEBUG_OUT printf( "01. The file '%s' was opened\n", argv[2] ); #endif fseek(fhEncrypt, 0, SEEK_END); cbEncrypt = sizeof(char)*ftell(fhEncrypt); rewind(fhEncrypt); // Выделяем память для чтения файла целиком и читаем его в эту область pbEncrypt = (BYTE*)malloc(cbEncrypt); if (pbEncrypt == NULL) HandleError("Out of memory. \n"); dwBytesRead = (DWORD)fread(pbEncrypt, 1, cbEncrypt, fhEncrypt); if (dwBytesRead != cbEncrypt) HandleError("The Encrypted file can not be reading from the 'EncryptionParam.bin'\n"); #ifdef DEBUG_OUT printf( "02. The file '%s' was readed, size = %d butes\n", argv[2], dwBytesRead ); #endif // Убираем разрывы строк, пробелы и другие спецсимволы - поля же файле е закодированы в base64, чтобы потом легчо было пропарсить pbClrContent = (BYTE*)malloc(cbEncrypt); if (pbClrContent == NULL) HandleError("Out of memory. \n"); for(i=0; i32)&&(pbEncrypt[i]<127)) pbClrContent[cbClrContent++] = pbEncrypt[i]; } #ifdef DEBUG_OUT printf( "\tClear size = %d bytes\n", cbClrContent ); #endif // Находим позиции сертификата FSS pbContent = (char *)strstr(pbClrContent, SearchStr1); if ( pbContent == NULL) HandleError("Not found Encoding parameters structure\n"); pos_Cert1 = pbContent-pbClrContent+1+strlen(SearchStr1); pbContent = (char *)strstr(pbClrContent, SearchStr2); if ( pbContent == NULL) HandleError("Not found End of Certificate\n"); pos_Cert2 = pbContent-pbClrContent+1; #ifdef DEBUG_OUT printf ("03. Found Certificate at positions %d - %d\n",pos_Cert1, pos_Cert2); #endif // Копируем сертификат в pbCert pbCert = base64_decode(&pbClrContent[pos_Cert1-1], pos_Cert2-pos_Cert1, &cbCert); #ifdef DEBUG_OUT // Для отладки пишем сертификат в файл. if(!(fhCertB64 = fopen("certificate.txt", "w+b" ))) HandleError( "Problem opening the file 'certificate.txt'\n" ); printf( "\tThe file 'certificate.txt' was opened\n" ); if(!fwrite( &pbClrContent[pos_Cert1-1], 1, pos_Cert2-pos_Cert1, fhCertB64)) HandleError( "The content can not be written to the 'certificate.txt'\n" ); printf( "\tThe content was written to the 'certificate.txt'\n" ); if(!(fhCert = fopen("certificate.bin", "w+b" ))) HandleError( "Problem opening the file 'certificate.bin'\n" ); printf( "\tThe file 'certificate.bin' was opened\n" ); if(!fwrite( pbCert, 1, cbCert, fhCert)) HandleError( "The content can not be written to the 'certificate.bin'\n" ); printf( "\tThe content was written to the 'certificate.bin'\n" ); #endif // Находим позиции параметров шифрования pbContent = (char *)strstr(pbClrContent, SearchStr2); if ( pbContent == NULL) HandleError("Not found Encoding parameters\n"); pos_EncP1 = pbContent-pbClrContent+1+strlen(SearchStr2); pbContent = (char *)strstr(pbClrContent, SearchStr3); if ( pbContent == NULL) HandleError("Not found End of Encoding parameters structure\n"); pos_EncP2 = pbContent-pbClrContent+1; #ifdef DEBUG_OUT printf ("04. Found Encoding parameters at positions %d - %d\n",pos_EncP1, pos_EncP2); #endif pbBufferB64 = base64_decode(&pbClrContent[pos_EncP1-1], pos_EncP2-pos_EncP1, &cbBufferB64); //----------------------------------------------- // Готовим блоб структуру //----------------------------------------------- cbBlobLenSimple = cbEncryptionParamSetStandart + sizeof(CRYPT_SIMPLEBLOB_HEADER) + SEANCE_VECTOR_LEN + G28147_KEYLEN + EXPORT_IMIT_SIZE; pbKeyBlobSimple = malloc(cbBlobLenSimple); if(!pbKeyBlobSimple) HandleError("Out of memory. \n"); //копируем неменяемый хедер в блобе memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->tSimpleBlobHeader, &tSimpleBlobHeaderStandart, sizeof(CRYPT_SIMPLEBLOB_HEADER)); //копируем неменяемую ASN структуру memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptionParamSet, pbEncryptionParamSetStandart, cbEncryptionParamSetStandart); // копируем session_SV memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bSV, &pbBufferB64[SV_OFFSET], SEANCE_VECTOR_LEN); // копируем session_EncryptedKey memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bEncryptedKey, &pbBufferB64[ENCRKEY_OFFSET], G28147_KEYLEN); // копируем session_MacKey memcpy(&((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bMacKey, &pbBufferB64[MACKEY_OFFSET], EXPORT_IMIT_SIZE); #ifdef DEBUG_OUT if(!(fhMacKey = fopen("bMacKey.txt", "w+b" ))) HandleError( "Problem opening the file 'bMacKey.txt'\n" ); printf( "\tThe file 'bMacKey.txt' was opened\n" ); if(!fwrite( ((CRYPT_SIMPLEBLOB*)pbKeyBlobSimple)->bMacKey, 1, EXPORT_IMIT_SIZE, fhMacKey)) HandleError( "The content can not be written to the 'bMacKey.txt'\n" ); printf( "\tThe content was written to the 'bMacKey.txt'\n"); printf( "\t\t%d bytes of tSimpleBlobHeader \n" "\t\t%d bytes of bEncryptionParamSet\n" "\t\t%d bytes of bSV\n" "\t\t%d bytes of bEncryptedKey\n" "\t\t%d bytes of bMacKey\n" "\t\t%d bytes of total copied\n", sizeof(CRYPT_SIMPLEBLOB_HEADER),cbEncryptionParamSetStandart,SEANCE_VECTOR_LEN,G28147_KEYLEN,EXPORT_IMIT_SIZE, cbBlobLenSimple ); if(!(fhKeyBlobSimple = fopen("pbKeyBlobSimple.txt", "w+b" ))) HandleError( "Problem opening the file 'pbKeyBlobSimple.txt'\n" ); printf( "\tThe file 'pbKeyBlobSimple.txt' was opened\n" ); if(!fwrite( pbKeyBlobSimple, 1, cbBlobLenSimple, fhKeyBlobSimple)) HandleError( "The content can not be written to the 'pbKeyBlobSimple.txt'\n" ); printf( "\tThe content was written to the 'pbKeyBlobSimple.txt'\n" ); #endif memcpy(&pPublicKeyBlob, &pbBufferB64[PUBKEY_OFFSET], PUBKEY_LEN); #ifdef DEBUG_OUT // для отладки пишем в файл. if(!(fhKeyBlob = fopen("pbKeyBlob.txt", "w+b" ))) HandleError( "Problem opening the file 'pbKeyBlob.txt'\n" ); printf( "\tThe file 'pbKeyBlob.txt' was opened\n" ); if(!fwrite( pPublicKeyBlob, 1, PUBKEY_LEN, fhKeyBlob)) HandleError( "The content can not be written to the 'pbKeyBlob.txt'\n" ); printf( "\tThe content was written to the 'pbKeyBlob.txt'\n"); if(!(fhEncodingParametersB64 = fopen("encoding_parameters.txt", "w+b" ))) HandleError( "Problem opening the file 'encoding_parameters.txt'\n" ); printf( "\tThe file 'encoding_parameters.txt' was opened\n" ); if(!fwrite( &pbClrContent[pos_EncP1-1], 1, pos_EncP2-pos_EncP1, fhEncodingParametersB64)) HandleError( "The content can not be written to the 'encoding_parameters.txt'\n" ); printf( "\tThe content was written to the 'encoding_parameters.txt'\n" ); if(!(fhEncodingParameters = fopen("encoding_parameters.bin", "w+b" ))) HandleError( "Problem opening the file 'encoding_parameters.bin'\n" ); printf( "\tThe file 'encoding_parameters.bin' was opened\n" ); if(!fwrite( pbBufferB64, 1, cbBufferB64, fhEncodingParameters)) HandleError( "The content can not be written to the 'encoding_parameters.bin'\n" ); printf( "\tThe content was written to the 'encoding_parameters.bin'\n" ); #endif // Находим основное зашифрованое содержимое pbContent = (char *)strstr(pbClrContent, SearchStr3); if ( pbContent == NULL ) HandleError("Not found Encoded content\n"); pos_Enc1 = pbContent-pbClrContent+1+strlen(SearchStr3); pbContent = (char *)strstr(pbClrContent, SearchStr4); if ( pbContent == NULL ) HandleError("Not found End of encoded content\n"); pos_Enc2 = pbContent-pbClrContent+1; #ifdef DEBUG_OUT printf ("05. Found Encoded content at positions %d - %d\n",pos_Enc1, pos_Enc2); #endif // В pbBufferB64 - первые 8 байт будут вектор инициализации, затем само зашифрованное содержимое pbBufferB64 = base64_decode(&pbClrContent[pos_Enc1-1], pos_Enc2-pos_Enc1, &cbBufferB64); #ifdef DEBUG_OUT // для отладки пишем в файл. if(!(fhEncodedContentB64 = fopen("encoded_content.txt", "w+b" ))) HandleError( "Problem opening the file 'encoded_content.txt'\n" ); printf( "\tThe file 'encoded_content.txt' was opened\n" ); if(!fwrite( &pbClrContent[pos_Enc1-1], 1, pos_Enc2-pos_Enc1, fhEncodedContentB64)) HandleError( "The content can not be written to the 'encoded_content.txt'\n" ); printf( "\tThe content was written to the 'encoded_content.txt'\n" ); if(!(fhEncodedContent = fopen("encoded_content.bin", "w+b" ))) HandleError( "Problem opening the file 'encoded_content.bin'\n" ); printf( "\tThe file 'encoded_content.bin' was opened\n" ); if(!fwrite( pbBufferB64, 1, cbBufferB64, fhEncodedContent)) HandleError( "The content can not be written to the 'encoded_content.bin'\n" ); printf( "\tThe content was written to the 'encoded_content.bin'\n" ); #endif // Чтение вектора инициализации из файла. memcpy(pbIV, pbBufferB64, dwIV); pbContent = &pbBufferB64[dwIV]; cbContent = cbBufferB64-dwIV; // ------------------------------------------------------------------ // Закончили чтение файла и подготовку структур - начинаем дешифровку // ------------------------------------------------------------------ if(!CryptAcquireContext( &hProv, argv[1], NULL, PROV_GOST_2012_256, 0)) HandleError("Error during CryptAcquireContext."); #ifdef DEBUG_OUT printf("06. The key container \"%s\" has been acquired. \n", argv[1]); #endif // Загрузка PUBLICKEYBLOB из сертификата, открытие файла, в котором содержится открытый ключ получателя. pCertContext = CertCreateCertificateContext ( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbCert, cbCert); if (!pCertContext) HandleError( "CertCreateCertificateContext" ); #ifdef DEBUG_OUT printf("07. Certificate context created\n"); #endif // Импортируем открытый ключ if (!CryptImportPublicKeyInfoEx( hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertContext->pCertInfo->SubjectPublicKeyInfo), 0, 0, NULL, &hPubKey)) HandleError( "CryptImportPublicKeyInfoEx" ); #ifdef DEBUG_OUT printf("08. Public key imported from cert file\n"); #endif // экспорт открытого ключа получателя в BLOB if (!CryptExportKey( hPubKey, 0, PUBLICKEYBLOB, 0, pbKeyBlob, &dwBlobLen)) HandleError( "CryptExportKey" ); #ifdef DEBUG_OUT printf("09. Public key exported to blob\n"); #endif memcpy(&pbKeyBlob[dwBlobLen-PUBKEY_LEN], pPublicKeyBlob, PUBKEY_LEN); // получение закрытого ключа if(!CryptGetUserKey( hProv, AT_KEYEXCHANGE, &hKey)) HandleError("Error during CryptGetUserKey private key."); #ifdef DEBUG_OUT printf("10. The private key has been acquired. \n"); #endif //получение ключа согласования импортом открытого ключа отправителя на закрытом ключе if(!CryptImportKey(hProv, pbKeyBlob, dwBlobLen, hKey, 0, &hAgreeKey)) HandleError("Error during CryptImportKey public key."); #ifdef DEBUG_OUT printf("11. The sender public key has been imported. \n"); #endif // установление PRO_EXPORT алгоритма ключа согласования ---не PRO12_EXPORT ! if(!CryptSetKeyParam( hAgreeKey, KP_ALGID, (LPBYTE)&ke_alg, 0)) HandleError("Error during CryptSetKeyParam agree key."); #ifdef DEBUG_OUT printf("12. PRO_EXPORT agree key algorithm has been set. \n"); #endif // Получение сессионного ключа импортом зашифрованного сессионного ключа на ключе согласования Agree. if(!CryptImportKey( hProv, pbKeyBlobSimple, cbBlobLenSimple, hAgreeKey, 0, &hSessionKey)) HandleError("Error during CryptImportKey session key."); #ifdef DEBUG_OUT printf("13. The session key has been imported. \n"); #endif // Установка вектора инициализации - без него первые 8 байт расшифруются неправильно. if(!CryptSetKeyParam( hSessionKey, KP_IV, pbIV, 0)) HandleError("Error during CryptSetKeyParam."); #ifdef DEBUG_OUT printf("14. CryptSetKeyParam setting IV succeeded. \n"); #endif // установка режима шифрования CBC if(!CryptSetKeyParam( hSessionKey, KP_MODE, (LPBYTE)&keyParam, 0)) HandleError("Error during CryptSetKeyParam."); #ifdef DEBUG_OUT printf("15. CryptSetKeyParam setting CBC mode succeeded. \n"); #endif // установка режима паддинга if(!CryptSetKeyParam( hSessionKey, KP_PADDING, (LPBYTE)&paddingMode, 0)) HandleError("Error during CryptSetKeyParam."); #ifdef DEBUG_OUT printf("16. CryptSetKeyParam setting ISO10126 padding succeeded. \n"); #endif #ifdef DEBUG_OUT if(!(fhContent = fopen("content.bin", "w+b" ))) HandleError( "Problem opening the file 'content.bin'\n" ); printf( "\tThe file 'content.bin' was opened\n" ); if(!fwrite( pbContent, 1, cbContent, fhContent)) HandleError( "The content can not be written to the 'content.bin'\n" ); printf( "\tThe content was written to the 'content.bin', size = %d\n", cbContent ); #endif // Дешифрование зашифрованного базового SOAP-запроса, после которого в // decryptedData будет содержать дешифрованные данные, // а decryptedDataLen - длину расшифрованных данных if(!CryptDecrypt( hSessionKey, 0, TRUE, 0, pbContent, &cbContent)) HandleError("Decryption failed."); #ifdef DEBUG_OUT printf("17. Decryption succeeded. \n"); #endif // Запись расшифрованного блока в файл. if(!(fhOutput = fopen(argv[3], "w+b" ))) HandleError( "Problem opening the file for encoded xml\n" ); #ifdef DEBUG_OUT printf( "\tThe file '%s' was opened\n", argv[3] ); #endif if(!fwrite( pbContent, 1, cbContent, fhOutput)) HandleError( "The decrypted content can not be written '\n" ); #ifdef DEBUG_OUT printf( "18. The decrypted content was written to the '%s'\n", argv[3] ); #endif CleanUp(); #ifdef DEBUG_OUT printf("The program ran to completion without error. \n"); #endif return 0; } void CleanUp(void) { if(fhEncrypt) fclose (fhEncrypt); if(fhOutput) fclose (fhOutput); #ifdef DEBUG_OUT if(fhCert) fclose (fhCert); if(fhCertB64) fclose (fhCertB64); if(fhMacKey) fclose (fhMacKey); if(fhKeyBlobSimple) fclose (fhKeyBlobSimple); if(fhKeyBlob) fclose (fhKeyBlob); if(fhEncodingParametersB64) fclose (fhEncodingParametersB64); if(fhEncodingParameters) fclose (fhEncodingParameters); if(fhEncodedContentB64) fclose (fhEncodedContentB64); if(fhEncodedContent) fclose (fhEncodedContent); if(fhContent) fclose (fhContent); #endif if(hKey) CryptDestroyKey(hKey); // Уничтожение дескриптора закрытого ключа. if(hSessionKey) CryptDestroyKey(hSessionKey); // Уничтожение дескриптора сессионного ключа. if(hAgreeKey) CryptDestroyKey(hAgreeKey); // Уничтожение дескриптора ключа согласования. if(hProv) CryptReleaseContext(hProv, 0); // Освобождение дескриптора провайдера. } BYTE *base64_decode(const BYTE *data, DWORD input_length, DWORD *output_length) { int i, j; DWORD sextet_a, sextet_b, sextet_c, sextet_d, triple; if (decoding_table == NULL) build_decoding_table(); if (input_length % 4 != 0) return NULL; *output_length = input_length / 4 * 3; if (data[input_length - 1] == '=') (*output_length)--; if (data[input_length - 2] == '=') (*output_length)--; BYTE *decoded_data = malloc(*output_length); if (decoded_data == NULL) return NULL; for (i = 0, j = 0; i < input_length;) { sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6); if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF; } return decoded_data; } void build_decoding_table() { int i; decoding_table = malloc(256); for (i = 0; i < 64; i++) decoding_table[(BYTE) encoding_table[i]] = i; } //-------------------------------------------------------------------- // В этом примере используется функция HandleError, функция обработки // простых ошибок, для печати сообщения и выхода из программы. // В большинстве приложений эта функция заменяется другой функцией, // которая выводит более полное сообщение об ошибке. void HandleError(char *s) { DWORD err = GetLastError(); printf("Error number : 0x%x\n", err); printf("Error description: %s\n", s); CleanUp(); if(!err) err = 1; exit(err); }