You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

libarchive加密压缩文件偶现自解压失败问题排查

Libarchive 加密压缩间歇性解压失败问题

问题概述

使用libarchive进行压缩加密操作时,会间歇性生成无法被libarchive自身解压解密的zip文件,但这类“异常”压缩包在终端中可正常解压。该问题需多次执行才会触发,多数情况下无异常。

测试场景

  • 测试用加密zip包仅包含一个内容为this is a test的文本文件,密码为password
  • 核心代码流程:
    1. 解压sample.zip;
    2. 使用原密码重新压缩加密(推测问题间歇性出现在此步骤);
    3. 再次解压(用于验证前一步是否出错)。

观测现象

  • 失败运行中,第二次解压的密码回调函数未遍历到正确密码(正确密码为列表中第4位的password);
  • 导致libarchive解压失败的压缩包可在终端正常解压,程序会将其保存为zip_that_breaks_decrypting.zip;
  • 若将该异常压缩包作为示例文件,libarchive每次都会解压失败。

疑惑点

有人指出“关闭初始解压的archive_entry后,后续加密复用该条目会导致数据失效”,但已在关闭前通过archive_entry_clone克隆了la_archive_entry到entry,仍出现随机性失败(运行500+次后才随机触发)。

复现代码

#include <iostream>
#include <stdint.h>
#include <vector>
#include <string.h>
#include <archive.h>
#include <archive_entry.h>
#include <fstream>
 
struct CallbackData {
    uint64_t idx{0};
    std::vector<std::string> passphrases;
    std::string last_passphrase_used{};
};
 
static const char* passphrase_callback(struct archive* /* archive */, void* client_data)
{
    std::cout << "inside passphrase callback" << std::endl;
    auto* cd = static_cast<CallbackData*>(client_data);
 
    if (cd->idx < cd->passphrases.size()) {
        std::cout << "testing passphrase: " << cd->passphrases[cd->idx] << std::endl;
        const char* ret = cd->passphrases[cd->idx].c_str();
        cd->last_passphrase_used = cd->passphrases[cd->idx];
        cd->idx++;
        return ret;
    }
    return nullptr;
}
 
int decrypt_wrapper(uint8_t* in_zip, const size_t& in_size, uint8_t*& unzip_buffer, size_t& read_file_size, std::string& guessed_pswd,
                                struct archive_entry*& entry, std::vector<int>& filters) {
    struct archive* la_archive{archive_read_new()};
    archive_read_support_format_zip(la_archive);
 
    struct archive_entry* la_archive_entry{nullptr};
 
    // Passphrase callback
    std::vector<std::string> pswds{"not it", "still not it", "infected", "password", "passphrase", "P@ssw0rd", "1234"};
    CallbackData cd{0, pswds, ""};
    archive_read_set_passphrase_callback(la_archive, static_cast<void*>(&cd), passphrase_callback);
 
    // Open archive file
    std::cout << "archive_read_open_memory result: " <<
    archive_read_open_memory(la_archive, reinterpret_cast<void*>(in_zip), in_size) << std::endl;
                                  
    // Filters
    for (int idx = 0; idx < archive_filter_count(la_archive); idx++) {
        filters.emplace_back(archive_filter_code(la_archive, idx));
    }
 
    constexpr ssize_t unarchived_max_size{30};
    unzip_buffer = new uint8_t[unarchived_max_size];
 
    // Header metadata
    archive_read_next_header(la_archive, &la_archive_entry);
    entry = archive_entry_clone(la_archive_entry);
 
    // Read archive data
    ssize_t read_bytes{0}; // store read return
    int64_t total_bytes_processed{0};
    while ((read_bytes = archive_read_data(la_archive, unzip_buffer + read_file_size,
                                           unarchived_max_size - read_file_size)) > 0) {
        // increment current size
        std::cout << "Looping - Inside archive_read_data loop. " << std::endl;
        read_file_size += static_cast<size_t>(read_bytes);
        total_bytes_processed += read_bytes;
    }
 
    if (read_bytes < 0) {
        std::cout << "------------------------------------- BAD RUN ----------------------------------" << std::endl;
    std::cout << "  Got an instance of archive_read_data() returning -30. Bad zip created with libarchive?" << std::endl;
    std::cout << "  Saving input file to zip_that_breaks_decrypting.zip. Expect this file to always make archive_read_data() fail and return -30" << std::endl;
        const std::string bad_name{"zip_that_breaks_decrypting.zip"};
        std::ofstream bad_zip(bad_name, std::ios::binary);
        bad_zip.write((char*)in_zip, in_size);
    bad_zip.close();
    return 1;
    }
 
    std::cout << "in_size: " << in_size << std::endl;
    std::cout << "format is zip?: " <<  (ARCHIVE_FORMAT_ZIP == archive_format(la_archive)) << std::endl;
    std::cout << "read_file_size: " << read_file_size << std::endl;
    std::cout << "read_bytes: " << read_bytes << std::endl;
    std::cout << "total_bytes_processed: " << total_bytes_processed << std::endl;
    std::cout << "last_password_used: " << cd.last_passphrase_used << std::endl;
 
    guessed_pswd = cd.last_passphrase_used;
                         
    archive_read_close(la_archive);
    archive_read_free(la_archive);
    return 0;
}
 
void encrypt_wrapper(uint8_t*& out_zip, size_t& out_zip_size, uint8_t*& decrypted_file,
                                      size_t& decrypted_file_size, const std::string& guessed_pswd,
                                      struct archive_entry*& entry, const std::vector<int>& archive_filters) {
 
    // Now we got a decrypted text file
    ///////////////////////////////////
 
    // Let's zip and encrypt it back
    struct archive* archive{archive_write_new()};
 
    for (int filter : archive_filters) {
        archive_write_add_filter(archive, filter);
    }
 
    archive_write_set_format(archive, ARCHIVE_FORMAT_ZIP);
    archive_write_set_options(archive, "zip:encryption=zipcrypt");
    archive_write_set_passphrase(archive, guessed_pswd.c_str());
       
    size_t data_size{20480};
    size_t buff_used{0};
    uint8_t* write_buffer[20480];
 
    archive_write_open_memory(archive, write_buffer, data_size, &buff_used);
    archive_entry_set_size(entry, decrypted_file_size);
    archive_write_header(archive, entry);
    archive_write_data(archive, decrypted_file, decrypted_file_size);
    archive_write_close(archive);
    archive_write_free(archive);
     
    uint8_t* out_buffer = new uint8_t[buff_used];
    memcpy(out_buffer, write_buffer, buff_used);
 
    out_zip = out_buffer;
    out_zip_size = buff_used;
}
 

int main() {
    std::cout << "----------------------------------- NEW RUN ---------------------------------" << std::endl;
    // Zip file to buffer
    std::ifstream original_zip("sample_file.zip", std::ios::binary);
    original_zip.seekg(0, std::ifstream::end);
    const size_t zip_file_size = original_zip.tellg();
    original_zip.seekg(0, std::ifstream::beg);
    uint8_t* original_in_zip = new uint8_t[zip_file_size];
    original_zip.read((char*)original_in_zip, zip_file_size);
    original_zip.close();
 

    // Return data from decrypt
    uint8_t* decrypted_file{nullptr};
    size_t decrypted_file_size{0};
    std::string guessed_pswd{};
    struct archive_entry* entry{nullptr};
    std::vector<int> archive_filters;
 
    decrypt_wrapper(original_in_zip, zip_file_size, decrypted_file, decrypted_file_size, guessed_pswd, entry, archive_filters);
 
    uint8_t* out_zip{nullptr};
    size_t out_zip_size{0};
    encrypt_wrapper(out_zip, out_zip_size, decrypted_file, decrypted_file_size, guessed_pswd, entry, archive_filters);
 
     
    uint8_t* decrypted_file2{nullptr};
    size_t decrypted_file_size2{0};
    struct archive_entry* entry2{nullptr};
    std::vector<int> archive_filters2;
    int returnVal{decrypt_wrapper(out_zip, out_zip_size, decrypted_file2, decrypted_file_size2, guessed_pswd, entry2, archive_filters2)};
 
    delete[] original_in_zip;
    return returnVal;
}

编译运行步骤

  • 将libarchive-src克隆至libarchive_question/build_materials目录;
  • 在libarchive_question/build_materials目录执行cmake与make;
  • 在libarchive_question目录执行以下命令:
    g++ zip_and_encrypt_bug.cpp -Ibuild_materials/libarchive-src/libarchive -Lbuild_materials/libarchive -larchive 
    export LD_LIBRARY_PATH=./build_materials/libarchive:$LD_LIBRARY_PATH
    while  ./a.out  ; do echo "heyo" ; done
    

程序会重复运行直至失败并终止循环。

内容的提问来源于stack exchange,提问作者Jorge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 07:20:57