为何ZipCrypto Key2暴力破解出现大量误报?代码问题排查
尝试利用CRC首字节与加密头最后一字节暴力破解ZipCrypto的Key2以测试耗时,却得到了2000余个无效的“有效密钥”。根据APPNOTE规范,Key2用于解密字节,由CRC32生成,是32位值,范围应为2147483648至4294967296。
本次测试参数:
- 密码:> 1234
- CRC值:> 77D7DCDE
- 测试流:
50 4B 03 04 14 00 01 00 08 00 68 99 6D 58 DE DC D7 77 CB 82 07 00 0F
B1 0A 00 09 00 00 00 6B 65 79 73 30 2E 74 78 74 50 3F 83 D5 C7 D4 69
6E 9B B9 E9 F2 DD 16 D5 EA 5B 41 F9 BC 59 6E 34 E8 2F 2B 49 4E DD 90
3E D8 65 5E 21 42 E6 8C 8C AD 8E
测试代码如下:
#ifdef LINUX #include <pthread.h> #include <stdlib.h> #include <unistd.h> typedef pthread_t thread; typedef pthread_mutex_t mutex; static inline void mutex_init(mutex *m) { int res = pthread_mutex_init(m, NULL); assert(res == 0); } static inline void mutex_lock(mutex *m) { int res = pthread_mutex_lock(m); assert(res == 0); } static inline void mutex_unlock(mutex *m) { int res = pthread_mutex_unlock(m); assert(res == 0); } static thread spawn_thread(void *(f)(void *), void *arg) { thread t; int res = pthread_create(&t, NULL, f, (void *)arg); if (res != 0) return (thread)NULL; return t; } static void join_thread(thread t) { pthread_join(t, NULL); } static size_t num_threads(void) { return (size_t)sysconf(_SC_NPROCESSORS_ONLN); } static uint64_t get_time(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } #endif unsigned char decrypt_byte(size_t Key2) { uint32_t dkey = Key2; unsigned short temp = dkey | 2; return (temp * (temp ^ 1)) >> 8; } static bool bytecheck(const unsigned char dat) { unsigned char targ = 0x77;//first byte of my Checksum if(dat == targ) { return true; } return false; } struct info { unsigned char buffer; size_t start; size_t end; }; // Stop when a thread finds a solution. static volatile bool stop = false; static void *worker(void *arg); static thread spawn_worker( size_t start, size_t end, unsigned char buffer) { struct info *info = (struct info *)malloc(sizeof(struct info)); assert(info != NULL); info->start = start; info->end = end; info->buffer = buffer; thread t = spawn_thread(worker, info); if (t == (thread)NULL) { fprintf(stderr, "error: failed to spawn thread"); exit(EXIT_FAILURE); } return t; } static void *worker(void *arg) { struct info *info = (struct info *)arg; size_t start = info->start; size_t end = info->end; unsigned char buf = info->buffer; free(info); for(size_t i =start; i < end+1; i++) { if (stop) return NULL; unsigned char res = decrypt_byte(i); unsigned char C = buf ^ res; if (bytecheck(C)) { printf("%zu\n",i); stop = true; return NULL; } } } //gcc ben.c -pg --std=gnu99 -lz -lm -lpthread -o zipcr int main() { size_t NUM_WORKERS = num_threads(); printf("threads = %lu\n", NUM_WORKERS); uint64_t t0 = get_time(); size_t tap = 2147483648; size_t port = tap / NUM_WORKERS; thread ts[NUM_WORKERS]; for (size_t i = 0; i < NUM_WORKERS; i++) { size_t start = tap+ (i*port); size_t end = start+port; unsigned char buffer = 0xF2; ts[i] = spawn_worker(start, end,buffer); } for (size_t i = 0; i < NUM_WORKERS; i++) join_thread(ts[i]); uint64_t t1 = get_time(); printf("\ntime = %lums\n", t1 - t0); return 0; }
请问为何会出现大量误报,代码中存在什么问题?
校验逻辑过于薄弱:仅验证单个解密字节等于CRC首字节(0x77),单字节匹配的概率为1/256。32位Key2范围包含2^31个可能值,理论上会产生约800万个匹配结果,你得到2000余个误报只是因为线程停止逻辑未拦截所有匹配,本质是单字节校验的误报率极高。必须校验完整CRC值或更多明文特征才能过滤无效密钥。
Key2范围理解错误:APPNOTE规范中Key2是32位无符号整数,范围应为0到4294967295,而非2147483648到4294967296。你仅遍历了高31位的一半,漏掉了低31位的所有可能值,既可能错过正确Key2,当前误报也都是错误范围内的无效匹配。
线程停止逻辑存在竞态条件:
stop作为volatile布尔值,多线程同时检测和设置时,可能有多个线程在stop被设置前进入匹配分支,导致多个无效密钥被打印。需用互斥锁保护stop的读写,或使用原子操作确保只有第一个匹配线程终止其他线程。decrypt_byte函数实现错误:ZipCrypto解密时,Key2会随解密过程更新(每次解密字节后用CRC32算法更新),你直接用原始Key2计算解密字节,未模拟该更新过程,导致解密结果本身错误,即使匹配0x77也是无效的。正确逻辑应为:用候选Key2解密第一个加密字节得到明文,再用明文更新Key2,继续解密后续字节直至验证完整CRC或更多特征。
加密字节选择错误:你使用的0xF2需确认是加密数据的第一个字节。Zip本地文件头中,加密数据从
filename字段后开始,若提取的字节位置错误,解密结果对应无效位置,自然产生大量误报。
内容的提问来源于stack exchange,提问作者terry franklin

