首页 > 解决方案 > 加密系统文件时程序崩溃

问题描述

我编写了这段代码来加密和解密文件夹的内容,这两个功能在加密普通文件时都能正常工作,但是当我将文件夹更改为系统文件夹时,程序崩溃,当我在崩溃前检查最新文件时,我无法打开一些它们(文件在另一个程序中打开),其中一些我无法进行更改。我正在处理我认为的所有错误,但是当它到达另一个程序打开的文件时它仍然会崩溃,如何解决这个问题以忽略这些类型的文件并继续而不是崩溃?我认为发布的结构并不重要。

char ListFiles(const wchar_t* folder, CIPHER* conf)
{
    wchar_t wildcard[MAX_PATH + 1];
    swprintf(wildcard, sizeof(wildcard) / sizeof(*wildcard), L"%s\\*", folder);
    WIN32_FIND_DATAW fd;
    HANDLE handle = FindFirstFileW(wildcard, &fd);
    if (handle == INVALID_HANDLE_VALUE) return 1;

    do
    {
        if (wcscmp(fd.cFileName, L".") == 0 || wcscmp(fd.cFileName, L"..") == 0)
            continue;

        wchar_t path[MAX_PATH + 1];
        swprintf(path, sizeof(path) / sizeof(*path), L"%s\\%s", folder, fd.cFileName);

        if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
            ListFiles(path, &conf);

        if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE && !(fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
        {
            wprintf(L"%s\n", path);

            FILE* f_dec;
            FILE* f_input = _wfopen(path, L"rb");
            FILE* f_enc = _wfopen(wcscat(path, L".encrypted"), L"wb");

            if (!f_input || !f_enc) {
                fprintf(stderr, "fopen error: %s\n", strerror(errno));
                continue;
            }

            conf->encrypt = 1; // encryption
            if (AES_L(conf, f_input, f_enc) != 0)
                continue;

            f_enc = _wfopen(path, L"rb");
            f_dec = _wfopen(wcscat(path, L".decrypted"), L"wb");
            if (!f_dec || !f_enc) {
                fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
                continue;
            }

            conf->encrypt = 0; // decryption
            if (AES_L(conf, f_enc, f_dec) != 0)
                continue;

            puts("\n\n");
        }

    } while (FindNextFileW(handle, &fd));

    FindClose(handle);
    return 0;
}

char AES_L(CIPHER* params, FILE* ifp, FILE* ofp)
{
    unsigned int inlen, outlen;
    unsigned char* inbuf = (unsigned char*)malloc(params->bufsize);
    unsigned char* outbuf = (unsigned char*)malloc(params->bufsize + EVP_MAX_BLOCK_LENGTH);

    if (inbuf == NULL || outbuf == NULL)
    {
        printf("memory cannot be allocated\n"); 
        cleanup(ifp, ofp, inbuf, outbuf);
        return 1;
    }

    EVP_CIPHER_CTX* ctx;
    ctx = EVP_CIPHER_CTX_new();
    if (ctx == NULL) {
        fprintf(stderr, "ERROR: EVP_CIPHER_CTX_new failed. OpenSSL error: %s\n",
            ERR_error_string(ERR_get_error(), NULL));
        cleanup(ifp, ofp, inbuf, outbuf);
        return 1;
    }
    if (!EVP_CipherInit_ex(ctx, params->cipher_type, NULL, params->key, params->iv, params->encrypt)) {
        fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n",
            ERR_error_string(ERR_get_error(), NULL));
        EVP_CIPHER_CTX_cleanup(ctx);
        cleanup(ifp, ofp, inbuf, outbuf);
        return 1;
    }

    while (1) {
        // Read in data in blocks until EOF. Update the ciphering with each read.
        inlen = fread(inbuf, sizeof(*inbuf), params->bufsize, ifp);
        if (ferror(ifp)) {
            fprintf(stderr, "ERROR: fread error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            cleanup(ifp, ofp, inbuf, outbuf, errno);
            return 1;
        }
        if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
            fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n",
                ERR_error_string(ERR_get_error(), NULL));
            EVP_CIPHER_CTX_cleanup(ctx);
            cleanup(ifp, ofp, inbuf, outbuf);
            return 1;
        }
        fwrite(outbuf, sizeof(*outbuf), outlen, ofp);
        if (ferror(ofp)) {
            fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            cleanup(ifp, ofp, inbuf, outbuf, errno);
            return 1;
        }
        if (inlen < params->bufsize) /* Reached End of file */
            break;
    }

    /* Now cipher the final block and write it out to file */
    if (!EVP_CipherFinal_ex(ctx, outbuf, &outlen)) {
        fprintf(stderr, "ERROR: EVP_CipherFinal_ex failed. OpenSSL error: %s\n",
            ERR_error_string(ERR_get_error(), NULL));
        EVP_CIPHER_CTX_cleanup(ctx);
        cleanup(ifp, ofp, inbuf, outbuf);
        return 1;
    }

    fwrite(outbuf, sizeof(*outbuf), outlen, ofp);
    if (ferror(ofp)) {
        fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
        EVP_CIPHER_CTX_cleanup(ctx);
        cleanup(ifp, ofp, inbuf, outbuf);
        return 1;
    }
    EVP_CIPHER_CTX_cleanup(ctx);
    cleanup(ifp, ofp, inbuf, outbuf);
    return 0;
}

更新:

void cleanup(FILE* ifp, FILE* ofp, unsigned char* inputBuf, unsigned char* outputBuf)
{
    free(inputBuf);
    free(outputBuf);
    fclose(ifp);
    fclose(ofp);
}

typedef struct {
    unsigned int key_size;
    unsigned int block_size;
    unsigned int bufsize;
    unsigned char* key;
    unsigned char* iv;
    unsigned int encrypt;
    const EVP_CIPHER* cipher_type;
} CIPHER;

标签: cwindowsopenssl

解决方案


我看到您的代码存在一些问题,尽管很难知道如果没有其余代码(例如,我们看不到该cleanup方法)或您如何创建和初始化它们是否会导致崩溃params

第一个问题是您可能正在泄漏文件句柄。当您打开其中的文件时,ListFiles您会成对打开它们,然后检查其中是否有NULL,如果有,则继续循环。

FILE* f_input = _wfopen(path, L"rb");
FILE* f_enc = _wfopen(wcscat(path, L".encrypted"), L"wb");

if (!f_input || !f_enc) {
    fprintf(stderr, "fopen error: %s\n", strerror(errno));
    continue;
}

如果f_input正确打开但f_enc失败怎么办?源文件将保持打开状态,直到程序结束。您应该分别检查它们中的每一个。

当您在以下位置分配内存时会出现类似的问题AES_L

unsigned char* inbuf = (unsigned char*)malloc(params->bufsize);
unsigned char* outbuf = (unsigned char*)malloc(params->bufsize + EVP_MAX_BLOCK_LENGTH);

if (inbuf == NULL || outbuf == NULL)
{
    printf("memory cannot be allocated\n");
    return 1;
}

如果其中一个缓冲区(可能inbufoutbuf崩溃,因为这会发生在内存已经非常低的情况下)。

当您为加密和解密文件创建路径时,可能会出现另一个问题。缓冲区的path大小MAX_PATH+1对于原始文件名来说已经足够了,但是随后您执行了一些wcscat操作,这些操作会导致将额外的数据添加到路径中。如果原来的文件名已经快到MAX_PATH极限了怎么办?当您执行时,wcscat您将溢出堆栈中的缓冲区,这也可能导致崩溃。

最后,ListFiles是递归的,所以如果有很多嵌套调用,你可能会用完堆栈,这也会导致崩溃(事实上,从我提到的问题来看,我认为这是主要的嫌疑人)。我会让它迭代。

无论如何,很难知道崩溃是否是由于这些问题造成的,最好的选择是在调试器中运行它。崩溃的错误消息会告诉你很多信息来确定原因。


推荐阅读