You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PKZIP密钥推导优化思路的C代码实现问题排查

PKZIP密钥推导优化思路的C代码实现问题排查

我正在尝试优化针对PKZIP的攻击方式,减少计算量和资源需求。PKZIP使用以下逻辑更新其密钥流或内部状态:

key0[i] = key0[i-1]>>8 ^ crctab[key0[i-1]&0xFF ^ plaintext[i]];
key1[i] = (key1[i-1] + (key0[i]&0xFF))* Const + 1;
key2[i] = key2[i-1]>>8 ^ crctab[key2[i-1]&0xFF ^ (key1[i]>>24)];

而生成字节密钥key3并将明文加密为密文的逻辑如下:

tmp = (key2[i] |3)&0xFFFF; // 注意:将key2的最后16位设为奇数
key3[i] = ((tmp*(tmp ^ 1))>>8)&0xFF;// 注意:等价于 ((x²)-x)//256 mod 256 或 (x*(x±1))//256 mod 256
ciphertext[i] = plaintext[i] ^ key3[i];

像pkcrack这类工具会利用key3[i]和key3[i-1]推导位置i处的2**22种可能的key2值,这个计算量实在太大。于是我发现了一个优化方向:

如果我们有连续3组明文和密文,就能推导出3个连续的key3值。每个key3值对应256种可能的key2低16位值,但只要找到key3[i]和key3[i-1]对应的16位值之间的关联,就能大幅压缩候选数量。

基于这个思路,我写了一段Python代码验证理论:

import math
n= int(65536)

x = 0xc90b # 这是 key2[i-1] |3
y = 0x2f17# 这是 key2[i] | 3

a = ((x*x) - x) % n # 为了简化,暂时不除以256
b = ((y*y) - y) % n # 因此a ^ b的结果是16位,等价于 (e<<8)
e = (a ^ b)&0xFF00

# 核心等式
'''
sqrt( ((x² -x) %n) ^ (256*e) ) == sqrt( ((y² -y) %n) )
也就是说,已知key3[i]和key3[i-1]就能算出e,而且每个位置的key2低16位奇数候选只有64个,
通过等式可以直接找到对应的y值
'''

i = round(math.sqrt((((x*x) - x) %n ^ e)))
j = round(math.sqrt(((y*y) - y) %n))

print(hex(a))
print(hex(b))
print(hex(e))
print(hex(i))
print(hex(j))

这段Python代码完美验证了我的理论,但当我把它转化为C代码并尝试实际应用时,遇到了问题:当我用生成的key2候选值去匹配算法生成的真实值时,总是找不到正确结果。我不确定是代码实现有误,还是对算法的理解出了偏差。

首先是我用来生成测试密文的C代码:

#include <time.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>


#define CRCPOLY 0xedb88320
#define CONSTx      0x08088405U /* 134775813 */
#define MSB(x)          (((x)>>24)&0xFF)
#define MSBMASK         0xFF000000U
#define CRC32(x,c)  (((x)>>8)^crctab[((x)^(c))&0xff])
 
uint32_t    crctab[256];

const uint8_t plain[8]= {
    0x64, 0x14, 0x9A, 0xC7, 0xB2, 0x96, 0xC0, 0x15  
};
uint8_t cipher[8]= {
    
};

uint8_t key3[8]= {
    
};

uint32_t k0 = 0x12345678,t0;
uint32_t k1 = 0x23456789,t1;
uint32_t k2 = 0x34567890,t2;

  static void mkCrcTab( ){
    unsigned int i, j, c;
    for( i = 0; i < 256; i++ )
    {
        c = i;
        for( j = 0; j < 8; j++ )
            if( c&1 )
            c = (c>>1) ^ CRCPOLY;
            else
            c = (c>>1);
        crctab[i] = c;
    }
}


int main(){
    
    mkCrcTab( );
    
    uint8_t pw[10] = {
        0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00
    };
    
    uint32_t ky0 = k0;  // FNV offset basis for 32-bit
    uint32_t ky1 = k1; // FNV prime for 32-bit
    uint32_t ky2 = k2;
    uint16_t tmp;
    uint8_t bt;

    for (int i = 0; i < 10; i++) {
        ky0 = CRC32( ky0, pw[i] );
        ky1 = (k1 + (ky0&0xff))*CONSTx + 1;
        ky2 = CRC32( ky2, MSB(ky1) );
        printf("%08X %08X %08X\n",ky0,ky1,ky2);
    }
    t0 =ky0;
    t1 = ky1;
    t2 = ky2;
    printf("===================================\n");
    for (int i = 0; i < 8; i++) {
        ky0 = CRC32( ky0, plain[i] );
        ky1 = (k1 + (ky0&0xff))*CONSTx + 1;
        ky2 = CRC32( ky2, MSB(ky1) );
        printf("%08X %08X %08X\n",ky0,ky1,ky2);
        tmp = ky2 | 3;
        bt = ((tmp*(tmp ^ 1))>>8)&0xFF;
        cipher[i] = plain[i] ^ bt;
        key3[i] = bt;
    }
    printf("===================================\n");
    printf("plain : ");
    for(int i =0; i <8; i++){
        printf("%02X ",plain[i]);
    }
    printf("\n");
    printf("cipher : ");
    for(int i =0; i <8; i++){
        printf("%02X ",cipher[i]);
    }
    printf("\n");
    printf("key3 : ");
    for(int i =0; i <8; i++){
        printf("%02X ",key3[i]);
    }
    printf("\n");
    printf("===================================\n");
    return 0;
}

我用密码1234567890加密了8字节明文,得到对应的密文。接着编写了以下代码来生成候选key2值:

#include <time.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

#define CRCPOLY 0xedb88320
#define CONSTx      0x08088405U /* 134775813 */
#define KEY2SPACE   (1<<12)
#define KEY3(i) (plain[(i)]^cipher[(i)])
#define MSB(x)          (((x)>>24)&0xFF)
#define MAXDELTA        (0x00FFFFFFU+0xFFU)
#define MSBMASK         0xFF000000U
#define CRC32(x,c)  (((x)>>8)^crctab[((x)^(c))&0xff])


uint32_t    crctab[256],crcbymsb[256],crcbylsb[256];
uint32_t    *key2i;
uint16_t    tmptaba[256],tmptabb[256],tmptabc[256],lkpc[65536],lkpa[65536];
uint8_t     idxbylsb[256], idxbymsb[256];
int      numKey2s = 0;

const uint8_t plain[8]= {
    0x64, 0x14, 0x9A, 0xC7, 0xB2, 0x96, 0xC0, 0x15
};
const uint8_t cipher[8]= {
    0xEF, 0x20, 0xDC, 0x4C, 0x11, 0x9A, 0x78, 0xEB
    
};

static void mkCrcTab( ){
    unsigned int i, j, c;
    for( i = 0; i < 256; i++ )
    {
        c = i;
        for( j = 0; j < 8; j++ )
            if( c&1 )
            c = (c>>1) ^ CRCPOLY;
            else
            c = (c>>1);
        crctab[i] = c;
        crcbymsb[c>>24] = c;
        crcbylsb[c&0xFF] = c;
        idxbymsb[c>>24] = i;
        idxbylsb[c&0xFF] = i;
    }
}

void generate( int n ){
    int i,j, d;
    uint8_t e, ea;
    uint32_t    cr[4],cr1[4];
    uint16_t    ls16a,ls16b,ls16c,eq,eq1;   
    printf("Generating possible key2_%d values...", n );
    ea = KEY3(n-1) ^ KEY3(n);
    e = KEY3(n-2) ^ KEY3(n-1);
    for( i = 3; i < 256; i+=4 ){
        ls16b = tmptabb[i];
        eq =  (int) sqrt( (double) ((256*ea) ^ (ls16b*(ls16b ^ 1))&0xFFFF));
        ls16c = lkpc[eq];
        cr[0] = crcbylsb[(ls16b>>8) ^ (ls16c&0xFF)];
        cr[1] = crcbylsb[(ls16b>>8) ^ ((ls16c-1)&0xFF)];
        cr[2] = crcbylsb[(ls16b>>8) ^ ((ls16c-2)&0xFF)];
        cr[3] = crcbylsb[(ls16b>>8) ^ ((ls16c-3)&0xFF)];
        eq1 = (int) sqrt( (double) ((256*e) ^ (ls16b*(ls16b ^ 1))&0xFFFF));
        ls16a = lkpa[eq1];
        cr1[0] = crcbylsb[(ls16a>>8) ^ (ls16b&0xFF)];
        cr1[1] = crcbylsb[(ls16a>>8) ^ ((ls16b-1)&0xFF)];
        cr1[2] = crcbylsb[(ls16a>>8) ^ ((ls16b-2)&0xFF)];
        cr1[3] = crcbylsb[(ls16a>>8) ^ ((ls16b-3)&0xFF)];
        for(j=0; j <4; j++){
            for(d=0; d < 4; d++){
                key2i[numKey2s++] = (((cr1[j]>>8) ^ cr[d])&0xFFFF0000) | (ls16c-d);
            }
        }
        
    }

    printf("done.\nFound %d possible key2-values.\n", numKey2s );
}

int main(){
    uint16_t kt,bk,idx;
    uint8_t dt;
    int x=0, y=0, z=0;
    
    key2i = malloc(KEY2SPACE * sizeof(uint32_t));
    if (!key2i) {
        perror("Memory allocation failed");
        exit(EXIT_FAILURE);
    }
    //load tmptabb
    for(uint16_t i=0; i < 0xFFFF; i++){
        kt = i | 3;
        dt = ((kt*(kt ^ 1))>>8)&0xFF;
        if(dt == KEY3(2)){
            tmptabb[y++] = i;
        }
        if(dt == KEY3(3)){
            tmptabc[z++] = i;
        }
        if(dt == KEY3(1)){
            tmptaba[x++] = i;
        }
    }
    //load lookup tables
    for(int k =3; k < 256; k+=4){
        bk = tmptaba[k];
        idx = (int) sqrt( (bk*(bk ^ 1))&0xFFFF);
        lkpa[idx] = bk;
        bk = tmptabc[k];
        idx = (int) sqrt( (bk*(bk ^ 1))&0xFFFF);
        lkpc[idx] = bk;
    }
    mkCrcTab();
    generate(3);
    for(int w =0; w < numKey2s; w++){
        printf("%08X\n", key2i[w]);
    }
}

我找不到问题出在哪里,这导致我无法继续研究key2和key0之间的数学关联。希望能得到帮助排查问题。


备注:内容来源于stack exchange,提问作者Alexio puk2sefu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 08:24:53