暗号zipを復元する
以前にも書きましたが、ZipCryptoはXORを使ったストリーム暗号形式なので、基本的に暗号化の逆のステップを踏めば、復号が可能です。暗号化の逆の手順を行う関数群は以下のようになります。
なお、パスワードが正しくなくても、データは間違った形ではあるものの変換は普通に行われるため、入力されたパスワードが正しいのかどうかは、複合化データと、ヘッダに記載されているCRC32値の比較をすることで判定することになります。
inline unsigned char zdecode(unsigned char n)
{
n ^= decrypt_byte();
update_keys(n);
return n;
}
void InitZipDecrypt(char *password, unsigned char cryptheader[])
{
key[0] = 305419896;
key[1] = 591751049;
key[2] = 878082192;
char *p = password;
while(*p != '\0') update_keys(*(p++));
for(int i = 0; i < CRYPTHEADLEN; i++) zdecode(cryptheader[i]);
}
void ZipDecrypt(unsigned char *buffer, int buflen)
{
for(int i = 0; i < buflen; i++){
buffer[i] = zdecode(buffer[i]);
}
}
次に示されるサンプルプログラムは以前に作成した圧縮zipファイルを復号・展開して、圧縮されていた文字列をコマンドプロンプトに出力するものです。これはあくまでも復号のサンプルであるため、汎用性がないことに注意してください。
void _tmain()
{
FILE *fp = _tfopen(_T("test.zip"), _T("rb"));
if(fp == NULL){
printf("ファイルが開けませんでした。");
return;
}
InitCRC32();
z_stream zs;
memset(&zs, 0, sizeof(z_stream));
inflateInit2(&zs, -15); // 2番目の引数は負の値にすること
const unsigned int buflen = 10000;
unsigned int signature, readlen, rest, have, crc32;
unsigned char buffer[buflen], infbuf[buflen], cryptheader[CRYPTHEADLEN];
char filename[260];
ZipHeader zipheader;
zipheader.filename = filename;
do{
// ヘッダタイプの判別
fread(&signature, sizeof(unsigned int), 1, fp);
if(signature != ZIPSIG_CENTDIR) break;
ReadZipHeader(fp, &zipheader);
printf("File name : %s\n", zipheader.filename);
// 復号ルーチンの初期化
if(zipheader.option & 0x1){
fread(cryptheader, 1, CRYPTHEADLEN, fp);
InitZipDecrypt("zip", cryptheader);
}
inflateReset(&zs);
rest = zipheader.compsize - CRYPTHEADLEN;
while(rest > 0){
crc32 = CRC32DEFAULT;
readlen = fread(buffer, 1, (rest > buflen) ? buflen : rest, fp);
rest -= readlen;
// 暗号の復号
if(zipheader.option & 0x1) ZipDecrypt(buffer, readlen);
// データの展開
zs.avail_in = readlen;
zs.next_in = buffer;
do{
zs.avail_out = buflen;
zs.next_out = infbuf;
inflate(&zs, Z_NO_FLUSH);
have = buflen - zs.avail_out;
// crc32値の計算
crc32 = GetCRC32(infbuf, have, crc32);
// 復号・展開後の文字列を画面に表示
for(unsigned int i = 0; i < have; i++) printf("%c", infbuf[i]);
}while(zs.next_out == 0);
// CRC32値が一致していなければ、復元できていないことになる
assert(zipheader.crc32 != crc32);
}
printf("\n\n");
}while(1);
inflateEnd(&zs);
fclose(fp);
}