zipファイルのパスワード解析ソフトを自作してみた(c++)

zipファイルのパスワード解析ソフトを自作してみた - bogamp’s blog
以前pythonでzipファイルのパスワード解析ソフトを作ったが、今度はc++で作ってみた。

#include <iostream>
#include <fstream>
#include <stdio.h>
#include <zip.h>
#include <vector>
#include <string>
#include <zlib.h>
#include <cstring>
#include <memory>

std::vector<std::string> read_file(std::string filename){//パスワードリストのファイルを読み込む関数
	std::string line;
	std::vector<std::string> content;
	content.reserve(1000000);//ここは無くても良いが、行数文だけ先に領域確保しておけば少し速くなる
	std::ifstream passfile(filename);
	if(passfile.is_open()){
		while(getline(passfile,line)){
			content.push_back(line);
		}
		passfile.close();
	}
	return content;
}

long calcrc(char data[],int len){//crc32は、zipファイルの誤り検出用の数字。これが一致すればファイル内容が正しいと確認できる。
	const void* voidata = data;
	long res = crc32(0L, NULL, 0);
	res = crc32(res, static_cast<const Bytef*>(voidata), len);//libzipにはcrc32を計算できる関数は無いようなので、zlibの関数を使う
	return res;
} 

int main(int argc, char *argv[]){
	auto passlist = read_file(argv[2]);//パスワードリストのファイルを読み込む
	auto archive = zip_open(argv[1],0,NULL);//zipファイルを開く
	zip_file_t * zfile = NULL;
	try{
		auto passnum = passlist.size();

		zip_stat_t stat;
		zip_stat_init(&stat);
		zip_stat_index(archive,0,0,&stat);//すべてのファイルのパスワードが同一だと仮定して、ここではindexが0のファイルを対象にする。
		auto filesize = stat.size;//ファイルサイズ
                long filecrc = (long)stat.crc;

		auto filetext = std::make_unique<char[]>(filesize);//読み込んだファイル内容を格納するためにメモリ確保

		for(int i=0;i<passnum;++i){
			auto pass = passlist.at(i);
			zfile = zip_fopen_index_encrypted(archive,0,0,pass.c_str());//openに失敗した場合、エラーを投げるのではなくNULLを返す仕様らしい
			if(zfile != NULL){
				int res =  zip_fread(zfile,filetext.get(),filesize);//resには読み込んだ文字列のサイズを返す。よってfilesizeと同じならおそらく成功したと判定できる。
				if(res==filesize){
					if(filecrc == calcrc(filetext.get(),res)){//crc32を計算してチェック
						std::cout << "answer : " + pass << std::endl;
						break;
					}
				}
				zip_fclose(zfile);
			}
		}
		zip_close(archive);
	}catch(...){
		if(zfile != NULL){
			zip_fclose(zfile);
		}
		zip_close(archive);
	}
}

参考:https://libzip.org/documentation/
libzipとzlibを使っているので、g++でコンパイル時は-lzipと-lzをリンクオプションでつける。

間違ったパスワードのときはzip_fopen_index_encryptedが必ず失敗してくれるなら簡単なのだが、zipの使用上無理らしい(試してみると1%くらいはopenに成功してしまう)ので、ファイル内容を読み込んでチェックする必要がある。
さすがにpythonよりはずいぶん速くなった。
C++初心者なのであんまり自信はないですが、スマートポインタとvectorを使っているので、メモリリークとかは多分ないはず…