over 9 years ago
海報探秘
先用 ImageMagick 來觀察一番。
$ identify post.png
post.png PNG 1003x654 1003x654+0+0 8-bit DirectClass 496KB 0.000u 0:00.000
毫無反應,就是張圖片?讓我們看更仔細一點。
$ identify -verbose post.png
identify: Extra compressed data. `post.png' @ warning/png.c/MagickPNGWarningHandler/1335.
identify: Extra compression data. `post.png' @ warning/png.c/MagickPNGWarningHandler/1335.
identify: Too many IDAT's found `post.png' @ error/png.c/MagickPNGErrorHandler/1309.
identify: corrupt image `post.png' @ error/png.c/ReadPNGImage/3294.
看起來後面有偷塞東西?認真讀個 PNG 的 spec,想辦法撈出多餘的資料。
#include <bits/stdc++.h>
#include <zlib.h>
typedef unsigned char Byte;
inline unsigned convert_uint(Byte *b) {
unsigned ret = 0;
for (int i = 0; i < 4; i++) ret = ret << 8 | b[i];
return ret;
}
Byte chunk_len_buf[4];
Byte chunk_name[4];
Byte chunk_data[8 << 10];
Byte raw_data[8 << 20];
int main(int argc, char *argv[]) {
FILE *fin = fopen(argv[1], "r");
FILE *fout = fopen(argv[2], "w");
fseek(fin, 8 + 4 + 4 + 13 + 4, SEEK_CUR); // skip header
z_stream zs;
memset(&zs, 0, sizeof(zs));
inflateInit(&zs);
zs.next_out = raw_data;
zs.avail_out = sizeof(raw_data);
while (fread(chunk_len_buf, 1, 4, fin) == 4) {
unsigned chunk_len = convert_uint(chunk_len_buf);
fread(chunk_name, 1, 4, fin);
fread(chunk_data, 1, chunk_len, fin);
fseek(fin, 4, SEEK_CUR); // skip crc
if (memcmp(chunk_name, "IDAT", 4) == 0) {
zs.next_in = chunk_data;
zs.avail_in = chunk_len;
inflate(&zs, Z_SYNC_FLUSH);
}
}
inflateEnd(&zs);
unsigned raw_png_len = 1003 * 654 * 4 + 654;
unsigned out_len = sizeof(raw_data) - zs.avail_out - raw_png_len;
fwrite(raw_data + raw_png_len, 1, out_len, fout);
fclose(fin);
fclose(fout);
return 0;
}
編譯並跑跑。
$ g++ poster.cpp -o poster -lz
$ ./poster post.png data
拿到些什麼呢?
$ file data
data: Gameboy ROM: "PAINT", [ROM ONLY], ROM: 256Kbit
歐歐歐,用 OpenEmu 跑起來。
這啥?Google 一下可以發現 Konami Code。
嘗試輸入 ↑ ↑ ↓ ↓ ← → ← → B A 後,它就開始畫 flag 了!
最後一步是想辦法辨別出 1lI
之間的差別,耶比 BCTF{1-l0v3-p14yIng-g4m3s}
。