libarchive加密压缩文件偶现自解压失败问题排查
Libarchive 加密压缩间歇性解压失败问题
问题概述
使用libarchive进行压缩加密操作时,会间歇性生成无法被libarchive自身解压解密的zip文件,但这类“异常”压缩包在终端中可正常解压。该问题需多次执行才会触发,多数情况下无异常。
测试场景
- 测试用加密zip包仅包含一个内容为
this is a test的文本文件,密码为password - 核心代码流程:
- 解压sample.zip;
- 使用原密码重新压缩加密(推测问题间歇性出现在此步骤);
- 再次解压(用于验证前一步是否出错)。
观测现象
- 失败运行中,第二次解压的密码回调函数未遍历到正确密码(正确密码为列表中第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
相关产品推荐
相关产品推荐

