常用算法反编译的代码

xor(a,b)

1
a^b = (~a & b) | (a & ~b)

exchange(a, b)

1
2
3
v3 = *(_DWORD *)(a);
*(_DWORD *)(a) = *(_DWORD *)(b);
*(_DWORD *)(b) = v3;
1
2
3
*(_DWORD *)(a2 + 32) ^= *(_DWORD *)(a2 + 24);
*(_DWORD *)(a2 + 24) ^= *(_DWORD *)(a2 + 32);
*(_DWORD *)(a2 + 32) ^= *(_DWORD *)(a2 + 24); //a ^= b b^=a a^=b

abs(a)

1
2
3
((*(_DWORD *)(a) >> 31) ^ *(_DWORD *)(a)) - (*(_DWORD *)(a) >> 31) //a为32位整数
// if a>0, a >> 31 = 0, so (a>>31) ^ a - (a>>31) = a = abs(a)
// if a<0, a >> 31 = 0xffffffff, so (a>>31) ^ a - (a>>31) = ~a - (-1) = ~a+1 = -a = abs(a)

rot13(str)

1
2
3
4
if ( (unsigned __int8)(v3 - 'a') <= 25u )
  v1[v2] = (v3 - 'T') % 26 + 'a';
if ( (unsigned __int8)(v3 - 'A') <= 25u )
  v1[v2] = (v3 - '4') % 26 + 'A';

快速幂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  for ( i = 0; i <= 32; i += 32 )
  {
    v3 = 65537i64;
    v5 = 1i64;
    v4 = (a1 >> i) & 0xFFFFFFFF;
    while ( v3 )
    {
      if ( v3 & 1 )
        v5 = v4 * v5 % 0xD6604BE5;
      v3 >>= 1;
      v4 = v4 * v4 % 0xD6604BE5;
    }
    v6 += v5 << i;
  }

按位连续保存数值(sample:cm1-swpuctf-2018)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl sub_402DE0(unsigned __int8 *a1, signed int a2, unsigned __int8 a3, int a4)
{ //a1之前已经放置的位数,a2当前需要放置数值的位数,a3当前需要放置的数值,a4放置的数组
  v7 = *a1;
  v4 = *a1 & 7;
  v6 = 8 - v4;
  if ( a2 > (unsigned __int8)(8 - v4) )         // a2>v6
  {
    *(_BYTE *)(a4 + ((signed int)v7 >> 3)) |= (signed int)a3 >> (a2 - v6);
    *(_BYTE *)(a4 + ((signed int)v7 >> 3) + 1) = a3 << (v6 + 8 - a2);// 8-(a2-v6)
  }
  else
  {
    *(_BYTE *)(a4 + ((signed int)v7 >> 3)) |= a3 << (v6 - a2);
  }
  result = a2 + *(_DWORD *)a1;
  *(_DWORD *)a1 = result;

单向散列算法

MD5算法

输入:任意长度,产生一个128bit的消息摘要。

1.用到4个变量(A,B,C,D)

1
2
3
4
5
6
7
__int64 __fastcall MD5Init(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{  
  *(_DWORD *)a4 = 0x67452301;
  *(_DWORD *)(a4 + 4) = 0xEFCDAB89;
  *(_DWORD *)(a4 + 8) = 0x98BADCFE;
  result = a4;
  *(_DWORD *)(a4 + 12) = 0x10325476;

2.每次以512bit=64byte为单位处理,用到的加法常数表T[i]=4294967296*abs(sin(i))的整数部分,其中i用弧度表示,通过正弦函数和幂函数进一步消除变换中的线性。T[1]=0xd76aa478,T[2]=0xe8c7b756,…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 __fastcall MD5Update(__int64 a1, __int64 a2, __int64 a3, __int64 a4, unsigned int a5)
{
  v6 = (*(_DWORD *)(a4 + 16) >> 3) & 0x3F;
  *(_DWORD *)(a4 + 16) += 8 * a5;
  if ( *(_DWORD *)(a4 + 16) < 8 * a5 )
    ++*(_DWORD *)(a4 + 20);
  *(_DWORD *)(a4 + 20) += a5 >> 29;
  if ( a5 < 64 - v6 )
  {
    i = 0;
  }
  else
  {
    MD5_memcpy(a1, a2, a3);
    MD5Transform(a1, a2, v8 + 24, v8);
    for ( i = 64 - v6; i + 63 < v10; i += 64 ) //每次以64byte为单位计算
      MD5Transform(a1, a2, v9 + i, v8);
  }
  return MD5_memcpy(a1, a2, (signed int)v9 + i); //剩下最后部分不足64位留待下次计算

3.最后一个单位不足56byte要补足56byte,等于或超过56byte,要补足120byte,以保证最后加上8byte的长度时刚好等于64byte。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall MD5Final(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  v9 = a4;
  v10 = a3;
  Encode(a1, a2, a3 + 16, (__int64)&v6, 8u);
  v8 = (*(_DWORD *)(v10 + 16) >> 3) & 0x3F;
  if ( (unsigned int)v8 > 55 )
    v4 = 120 - v8; //等于或超过56byte
  else
    v4 = 56 - v8; //不足56byte
  v7 = v4;
  MD5Update(a1, a2, (__int64)&PADDING, v10, v4); //以0b10...方式补足,PADDING=0x8000...
  MD5Update(a1, a2, (__int64)&v6, v10, 8u); //加上8byte长度
  Encode(a1, a2, v10, v9, 0x10u); //将ABCD级联作为结果保存在v9=v4

因此,可以通过识别是否具有64个常量元素表T来确定是否MD5算法,常见的变形方法:

  1. 改变初始化的4个常数
  2. 改变填充方式
  3. 改变Hash变换过程MD5Transform

MD4算法

框架与MD5算法相同,MD5比MD4改进之处:

  1. 加入了第四轮
  2. 每一步都有唯一的加法常数T[i]
  3. 第二轮中的G函数从((X ∧ Y) ∨ (X ∧ Z) ∨ (Y ∧ Z)) 变为 ((X ∧ Z) ∨ (Y ∧ ~Z))以减小其对称性
  4. 每一步都加入了前一步的结果,以加快”雪崩效应”
  5. 改变了第2轮和第3轮中访问输入子分组的顺序,减小了形式的相似程度
  6. 近似优化了每轮的循环左移位移量,以期加快”雪崩效应”,各轮的循环左移都不同

MD2算法

框架与MD5算法相同,区别:

  1. 初始化为16byte的0字节,对应MD5的4个常数ABCD
  2. 使用256byte的PI_SUBST用于异或计算,前8个字节为:292E43C9A2D87C01
  3. 每次以16byte为单位处理,每单位运算18round
  4. 填充方式,以8进制字节

SHA1算法

输入:任意长度,产生一个160bit=20byte的消息摘要。其变种对应更长的消息摘要,以它们的摘要长度(以位计算)加在原名后面来命名:SHA-224、SHA-256、SHA-384,和SHA-512,并称为SHA-2

1.初始化散列值,5个32位常量

1
2
3
4
5
6
7
8
signed __int64 __fastcall SHA1Reset(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
    *(_DWORD *)a4 = 0x67452301;
    *(_DWORD *)(a4 + 4) = 0xEFCDAB89;
    *(_DWORD *)(a4 + 8) = 0x98BADCFE;
    *(_DWORD *)(a4 + 12) = 0x10325476;
    *(_DWORD *)(a4 + 16) = 0xC3D2E1F0;
}

2.每次以64byte为单位处理,每单元SHA1ProcessMessageBlock使用常量0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
signed __int64 __fastcall SHA1Input(__int64 a1, __int64 a2, _BYTE *a3, __int64 a4, int a5)
{
        while ( 1 )
        {
          v7 = v10--;
          if ( !v7 || *(_DWORD *)(v8 + 100) )
            break;
          v6 = *(_WORD *)(v8 + 28);
          *(_WORD *)(v8 + 28) = v6 + 1;
          *(_BYTE *)(v8 + v6 + 30) = *v9;
          *(_DWORD *)(v8 + 20) += 8;
          if ( !*(_DWORD *)(v8 + 20) && !++*(_DWORD *)(v8 + 24) )
            *(_DWORD *)(v8 + 100) = 1;
          if ( *(_WORD *)(v8 + 28) == 64 ) //每次以64byte为单位计算
            SHA1ProcessMessageBlock(a1);
          ++v9;
        }

3.类似MD5,最后一个单位不足56byte要补足56byte,等于或超过56byte,要补足120byte,以保证最后加上8byte的长度时刚好等于64byte。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__int64 __fastcall SHA1PadMessage(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  if ( *(_WORD *)(a4 + 28) <= 55 )
  else
  {
    v4 = *(_WORD *)(a4 + 28);
    *(_WORD *)(a4 + 28) = v4 + 1;
    v5 = a4;
    *(_BYTE *)(a4 + v4 + 30) = 0x80u; //以0b10...方式补足,PADDING=0x8000...
    while ( *(_WORD *)(a4 + 28) <= 63 )
    {
      *(_BYTE *)(a4 + v6 + 30) = 0;
    }
    SHA1ProcessMessageBlock(a1, a2, v5, a4);
    while ( *(_WORD *)(v12 + 28) <= 55 )
    {
      *(_BYTE *)(v12 + v7 + 30) = 0;
    }
  }
  *(_BYTE *)(v12 + 86) = *(_DWORD *)(v12 + 24) >> 24; //加上8byte的长度
  *(_BYTE *)(v12 + 87) = *(_DWORD *)(v12 + 24) >> 16;
  *(_BYTE *)(v12 + 88) = *(_WORD *)(v12 + 24) >> 8;
  *(_BYTE *)(v12 + 89) = *(_DWORD *)(v12 + 24);
  *(_BYTE *)(v12 + 90) = *(_DWORD *)(v12 + 20) >> 24;
  *(_BYTE *)(v12 + 91) = *(_DWORD *)(v12 + 20) >> 16;
  *(_BYTE *)(v12 + 92) = *(_WORD *)(v12 + 20) >> 8;
  v10 = *(_DWORD *)(v12 + 20);
  *(_BYTE *)(v12 + 93) = v10;
  return SHA1ProcessMessageBlock(a1, a2, v10, v12);

SM3国密算法

输入:任意长度,产生一个256bit=32byte的消息摘要(类似SHA-256)。

  • 初始常量:{0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600, 0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E},每次以64byte为单位处理
  • 加密过程中用到常量Tj={ 0x79CC4519    0≤j≤15;0x7A879D8A 16≤j≤63}
  • 填充方式类似MD5,最后一个单位不足56byte要补足56byte,等于或超过56byte,要补足120byte,以保证最后加上8byte的长度时刚好等于64byte

CRC32(Cyclic Redundancy Checksum,循环冗余校验码)算法

利用CRC32多项式值:0x04C11DB7或者颠倒后的0xEDB88320,生成CRC32表,再与明文异或查表即可。同理,CRC16采用多项式值:0X8005和0XA001。

1
2
3
4
5
6
7
8
9
10
11
12
__int64 __fastcall crc32_for_byte(__int64 a1, __int64 a2, __int64 a3, int a4)
{
  for ( i = 0; i <= 7; ++i )
  {
    if ( v7 & 1 )
      v4 = 0;
    else
      v4 = 0xEDB88320;
    v7 = ((unsigned int)v7 >> 1) ^ v4; //r = (r & 1? 0: (uint32_t)0xEDB88320L) ^ r >> 1;
  }
  return v7 ^ 0xFF000000;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned __int64 __fastcall crc32(__int64 a1, __int64 a2, unsigned __int64 a3, __int64 a4, int *a5)
{
  if ( !table_4361[0] )
  {
    for ( i = 0LL; i <= 0xFF; ++i )
    {
      table_4361[i] = crc32_for_byte(a1, a2, a3, i);
    }
  }
  for ( j = 0LL; j < v10; ++j )
  {
    *v11 = ((unsigned int)*v11 >> 8) ^ table_4361[(unsigned __int64)(unsigned __int8)(*v11 ^ *(_BYTE *)(v9 + j))]; //*crc = table[(uint8_t)*crc ^ ((uint8_t*)data)[i]] ^ *crc >> 8;
  }
}

Adler-32校验算法

Adler-32通过求解两个16位的数值A、B实现,并将结果连结成一个32位整数。A就是字符串中每个字节的和,而B是A在相加时每一步的阶段值之和。在Adler-32开始运行时,A初始化为1,B初始化为0,最后的校验和要模上65521=0xFFF1(2**16范围内最大的素数)

1
2
3
4
5
6
7
8
9
__int64 __fastcall adler32raw(__int64 a1, __int64 a2, int a3, __int64 a4)
{
  for ( i = 0; i < a3; ++i )
  {
    v7 = (*(_BYTE *)(i + a4) + v7) % 0xFFF1; //a = (a + data[index]) % MOD_ADLER;
    v6 = (v6 + v7) % 0xFFF1; //b = (b + a) % MOD_ADLER;
  }
  return v7 | (v6 << 16);
}

对称加密算法

流加密算法

RC4流加密算法

密钥长度不限,一般不超过256byte。借助由密钥初始化的长度为256byte的Sbox数组创建伪随机流,与明文逐字节异或,解密时使用相同算法。密钥和明文均以字节数组char inbuf[1024]方式读入。

1
2
3
4
5
6
7
8
9
10
__int64 __fastcall rc4_init(__int64 a1, __int64 a2, __int64 a3, __int64 a4, signed int a5)
{
  for ( i = 0; i <= 255; ++i )
    *(_BYTE *)(a4 + i) = i; //Sbox[256]初始化为0-255
  while ( v7 <= 255 )
  {
    v8 += *(_BYTE *)(v9 + v7) + *(_BYTE *)(v7 % v11 + v10); //j += Sbox[i] + key[i % keylen];
    result = (__int64)swap_bytes(a1, a2, (char *)(v9 + v8), (char *)(v7++ + v9)); //swap(Sbox[i], Sbox[j])
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall rc4_crypt(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, unsigned int a6)
{
  for ( i = 0; ; ++i )
  {
    if ( (signed int)i >= (signed int)v11 )
      break;
    *(_BYTE *)(v8 + 257) += *(_BYTE *)(v8 + ++*(_BYTE *)(v8 + 256)); //j+=Sbox[i]
    swap_bytes(a1, a2, (char *)(v8 + *(_BYTE *)(v8 + 257)), (char *)(*(_BYTE *)(v8 + 256) + v8)); //swap(Sbox[i], Sbox[j])
    *(_BYTE *)((signed int)i + v10) = *(_BYTE *)((signed int)i + v9) ^ *(_BYTE *)(v8
                                                                                + (unsigned __int8)(*(_BYTE *)(v8 + *(_BYTE *)(v8 + 256))
                                                                                                  + *(_BYTE *)(v8 + *(_BYTE *)(v8 + 257)))); //t=Sbox[i] + Sbox[j]; out[i]=in[i]^Sbox[t]
  }
}

Salsa20算法

将32Byte的key和8Byte的随机数nonce扩展为2^70 Byte的随机字节流。一次生成一个64字节的block,每一个block是通过将key、nonce和block number以及部分常量组成64字节的input,通过核函数,输出64字节的output。最终多个block组成长度为2^70的随机字节流,在生成过程中,每个block相互独立。识别方法:常量0x61707865,0x3320646e,0x79622d32,0x6b206574。

块加密算法

将明文按一定长度分块进行加密运算,得到密文块。主要有ECB(Electronic codebook,电码本模式)和CBC(Cipher Block Chaining,密文分组链接模式)模式,此外还有CFB(Cipher Feedback,密文反馈模式)和OFB(Output Feedback,输出反馈模式)。

CBC需要设定初始值iv,每组明文先与iv异或再进行ECB加密,并将当前密文作为下一组明文的iv。

1
2
3
4
5
6
7
8
9
10
11
int __fastcall AES_CBC_encrypt_buffer(__int64 a1, __int64 a2, signed __int64 a3, __int64 a4, unsigned int a5)
{
  for ( i = 0LL; v10 > i; i += 16LL )
  {
    XorWithIv(a1, a2); //明文先与iv异或
    Cipher(a1, a2); //调用ECB加密
    v6 = v9; //将当前密文作为下一组明文iv
    v9 += 16LL;
  }
  return memcpy(a1, a2, v6, v8 + 176, 16LL); //保存iv以便下一次调用
}

IDEA(International Data Encryption Algorithm)算法

密钥key长度128bit=16byte,以16bit整形数组uint16_t key[8]方式读入,产生52=8×6+4个子密钥。明文以64bit=8byte为一组,以64bit长整型数组uint64_t plaintext[1024]方式读入,分成4个16-位子分组:X1,X2,X3和X4。这4个子分组成为算法的第一轮的输入,总共有8轮。在每一轮中,这4个子分组相互异或,相加,相乘,且与6个16-位子密钥相异或,相加,相乘。解密过程类似,使用的子密钥对应加密时所使用子密钥的逆元,并且需要逆序使用。

1
2
3
4
5
6
7
8
9
10
11
12
  v14 = subkey_generation(a2, (__int64)v8);
  for ( i = 0; i <= 7; ++i ) //8轮相同的加密运算,使用8*6个子密钥
  {
    idea_round(v13, &v8[12 * i], (__int64)&v4);
    for ( j = 0; j <= 3; ++j )
      v13[j] = *(&v4 + j);
  }
  swap(&v5, &v6); //第9轮输出变换,使用最后4个子密钥
  v4 = mp_mod(v4, v9);
  v5 = add_mod(v5, v10);
  v6 = add_mod(v6, v11);
  v7 = mp_mod(v7, v12);
1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall subkey_generation(__int64 a1, int a2, __int64 a3, __int64 a4)
{
  for ( i = 0; i <= 7; ++i )
    v5[i] = *(_WORD *)(2LL * i + a4); //key直接分成前8个16bit子密钥
  for ( i = 0; i <= 5; ++i )
  {
    for ( j = 0; j <= 7; ++j )
      *(_WORD *)(v8 + 2LL * (8 * i + j)) = v5[j];
    left_shift(a1, a2); //依次左循环25bit,分成5*8个16bit子密钥
  }
  for ( i = 0; i <= 3; ++i )
    *(_WORD *)(v8 + 2LL * (i + 48)) = v5[i]; //左循环25bit,取4个16bit作为最后4个子密钥
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall idea_round(_WORD *a1, _WORD *a2, __int64 a3)
{
  v8 = mp_mod(*a1, *a2); //step 1. multiply X1 by 1st sub key
  v9 = add_mod(v13[1], v14[1]); //step 2. add X2 to 2nd sub key
  v10 = add_mod(v13[2], v14[2]); //step 3. add X3 to 3rd sub key
  v11 = mp_mod(v13[3], v14[3]); //step 4. multiply X4 by 4th sub key
  v6 = XOR(v8, v10); //step 5. XOR results in step 1 and step 3
  v7 = XOR(v9, v11); //step 6. XOR results in step 2 and step 4
  v12 = MA(&v6, v14 + 4, &v4); //step 7. MA diffusion with 5th and 6th sub key
  *(_WORD *)v15 = XOR(v8, v5); //step 8. generate the output
  *(_WORD *)(v15 + 2) = XOR(v9, v4);
  *(_WORD *)(v15 + 4) = XOR(v10, v5);
  *(_WORD *)(v15 + 6) = XOR(v11, v4);
  swap(v15 + 2, v15 + 4);
  return v12;
}

TEA(Tiny Encryption Algorithm)算法

密钥长度128bit=16byte,以32bit整形数组unsigned long key[16]方式读入,分为4个32bit子密钥K[4]。明文以64bit=8byte为一组,以32bit整形数组unsigned long data[1024]方式读入,分为2个32bit整形v[2],经过32次循环加密,借助密钥常量:0x9e3779b9(黄金分割点,delta=sqr(5)-1 * 2^31),解密算法是加密算法的逆过程。XTEA是TEA的升级版,增加了更多的密钥表,移位和异或操作。识别方法:密钥常量0x9e3779b9(-0x61C88647 )。

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall tea_encrypt_block(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  for ( i = 0; i <= 0x1F; ++i ) //32次循环
  {
    v6 -= -0x9E3779B9; //DELTA常量
    //v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
    //v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
    v8 += (v7 + v6) ^ (16 * v7 + *(_DWORD *)a3) ^ ((v7 >> 5) + *(_DWORD *)(a3 + 4));
    v7 += (v8 + v6) ^ (16 * v8 + *(_DWORD *)(a3 + 8)) ^ ((v8 >> 5) + *(_DWORD *)(a3 + 12));
  }
}
1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall xtea_encrypt_block(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  for ( i = 0; i <= 0x1F; ++i )
  {
    //v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
    v7 += (((v6 >> 5) ^ 16 * v6) + v6) ^ (*(_DWORD *)(4LL * (v5 & 3) + a3) + v5);
    v5 -= -0x9E3779B9;
    //v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]);
    v6 += (((v7 >> 5) ^ 16 * v7) + v7) ^ (*(_DWORD *)(4LL * ((v5 >> 11) & 3) + a3) + v5);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define XXTEA_MX (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z)
#define XXTEA_DELTA 0x9e3779b9
static void xxtea_long_encrypt(xxtea_long *v, xxtea_long len, xxtea_long *k)
{
    xxtea_long n = len - 1;
    xxtea_long z = v[n], y = v[0], p, q = 6 + 52 / (n + 1), sum = 0, e;
    if (n < 1) {
        return;
    }
    while (0 < q--) {
        sum += XXTEA_DELTA;
        e = sum >> 2 & 3;
        for (p = 0; p < n; p++) {
            y = v[p + 1];
            z = v[p] += XXTEA_MX;
        }
        y = v[0];
        z = v[n] += XXTEA_MX;
    }
}

BlowFish算法

密钥长度4-56byte,以字节数组uint8_t key[60]方式读入。明文以64bit=8byte为一组,以32bit整形数组uint32_t data[1024]方式读入,分为两个32位整型L,R。识别方法:uint32_t P[16 + 2] = {0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L,…和uint32_t Sbox[4*256] = {0xD1310BA6L, 0x98DFB5ACL, 0x2FFD72DBL, 0xD01ADFB7L,…

  • 密钥扩展部分借助常量数组P[18]和S-box[4][256](使用常数pi的小数部分初始化),将最长56byte的密钥转化为共(18+1024)*4=4168byte的子密钥。
  • 数据加密部分共16轮,与子密钥进行相应异或运算。
  • 解密过程相同,只是以相反顺序使用P[18]数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
__int64 __fastcall Blowfish_Init(__int64 a1, __int64 a2, __int64 a3, __int64 a4, int a5)
{
  for ( i = 0; i <= 17; ++i )
  {
    v7 = 0;
    for ( k = 0; k <= 3; ++k )
    {
      v7 = (v7 << 8) | *(_BYTE *)(j++ + a3);
      if ( j >= a5 )
        j = 0;
    }
    result = a4;
    *(_DWORD *)(a4 + 4LL * i) = v7 ^ HAVAL__00405000[i]; //P数组与key异或
  }
  v6 = 0; //datal = 0x00000000; datar = 0x00000000;对全0的data进行加密初始化子密钥
  for ( i = 0; i <= 17; i += 2 )
  {
    Blowfish_Encrypt(a1, a2, (__int64)&v6); //Blowfish_Encrypt(ctx, &datal, &datar);
    *(_DWORD *)(v11 + 4LL * i) = v6;  //P[i] = datal;
    result = v11;
    *(_DWORD *)(v11 + 4LL * (i + 1)) = 0; //P[i + 1] = datar;
  }
  for ( i = 0; i <= 3; ++i )
  {
    for ( j = 0; j <= 255; j += 2 )
    {
      Blowfish_Encrypt(a1, a2, (__int64)&v6); //Blowfish_Encrypt(ctx, &datal, &datar);
      *(_DWORD *)(v11 + 4 * (((signed __int64)i << 8) + j + 16) + 8) = v6; //S[i][j] = datal;
      result = v11;
      *(_DWORD *)(v11 + 4 * (((signed __int64)i << 8) + j + 1 + 16) + 8) = 0; //S[i][j + 1] = datar;
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
int *__fastcall Blowfish_Encrypt(__int64 a1, int a2, int *a3, __int64 a4, int *a5)
{
  for ( i = 0; i <= 15; ++i )
  {
    v5 = *(_DWORD *)(v13 + 4LL * i) ^ v12; //R[i] = L[i-1] ^ P[i]
    v12 = (unsigned __int64)F(a1, a2) ^ v11; //L[i] = F(R[i]) ^ R[i-1]
    v11 = v5;
  }
  v8 = *(_DWORD *)(v13 + 64) ^ v6; //L[17] = R[16] ^ P[18]
  *v14 = *(_DWORD *)(v13 + 68) ^ v7; //R[17] = L[16] ^ P[17]
}

DES(Data Encryption Standard)算法

密钥长度56bit=7byte,附加8bit(第8,16,24,32,40,48,56,64)奇偶校验位。明文以64bit=8byte为一组,分为2个32bit整形。密钥和明文均以字节数组unsigned char key[8]方式读入。

  • 初始化子密钥:将密钥按置换函数PC-1(8*7)表进行压缩置换,变成56bit,分成前28bit和后28bit的C0/D0,再将其进行16轮的循环左移以及压缩置换PC-2(8*6),最后生成16个48bit的子密钥。
  • 加密过程:每8byte为一组,通过初始置换,然后与子密钥数组进行16轮迭代运算,再经过终结置换得到密文。L_n = R_n-1;R_n = L_n-1 ^ P(S(E(R_n-1) ^ k_n-1)),R_n的计算由四步运算构成:秘钥置换(Kn的生成,n=0~16);E-盒扩展置换;S-盒代替;P-盒置换。
  • 解密过程类似,使用逆序的子密钥。
1
2
3
4
5
6
7
8
9
10
11
12
unsigned int *__fastcall des_key_schedule(__int64 a1, __int64 a2, unsigned int *a3, __int64 a4)
{
  v6 = (v4 ^ (v5 >> 4)) & 0xF0F0F0F; //DO_PERMUTATION (right, work, left, 4, 0x0f0f0f0f)
  v9 = (v8 ^ v7) & 0x10101010; //DO_PERMUTATION (right, work, left, 0, 0x10101010)
  v17 = (32 * leftkey_swap[(unsigned __int64)(((unsigned int)v10 >> 21) & 0xF)] | (leftkey_swap[(unsigned __int64)(((unsigned int)v10 >> 13) & 0xF)] << 6) | (leftkey_swap[(unsigned __int64)(((unsigned int)v10 >> 5) & 0xF)] << 7) | leftkey_swap[(unsigned __int64)(((unsigned int)v10 >> 24) & 0xF)] | 2 * leftkey_swap[(unsigned __int64)(((unsigned int)v10 >> 16) & 0xF)] | 4 * leftkey_swap[(unsigned __int64)(((unsigned int)v10 >> 8) & 0xF)] | 8 * leftkey_swap[(unsigned __int64)(v10 & 0xF)] | 16 * leftkey_swap[(unsigned __int64)((unsigned int)v10 >> 29)]) & 0xFFFFFFF;
  v12 = 32 * rightkey_swap[(unsigned __int64)(((unsigned int)v11 >> 20) & 0xF)] | (rightkey_swap[(unsigned __int64)((unsigned __int16)v11 >> 12)] << 6) | (rightkey_swap[(unsigned __int64)((unsigned __int8)v11 >> 4)] << 7) | rightkey_swap[(unsigned __int64)(((unsigned int)v11 >> 25) & 0xF)] | 2 * rightkey_swap[(unsigned __int64)(((unsigned int)v11 >> 17) & 0xF)] | 4 * rightkey_swap[(unsigned __int64)(((unsigned int)v11 >> 9) & 0xF)] | 8 * rightkey_swap[(unsigned __int64)(((unsigned int)v11 >> 1) & 0xF)]; //压缩置换
  for ( i = 0; i <= 15; ++i ) //16轮循环左移及压缩置换,生成16个48bit子密钥
  {
    *v18 = ((unsigned int)v16 >> 26) & 2 | ((unsigned int)v16 >> 18) & 4 | ((unsigned int)v16 >> 3) & 8 | ((unsigned int)v16 >> 10) & 0x10 | ((unsigned int)v16 >> 5) & 0x20 | v16 & 0x100 | ((unsigned int)v16 >> 14) & 0x200 | ((unsigned int)v16 >> 1) & 0x400 | ((_WORD)v16 << 6) & 0x800 | ((unsigned int)v16 >> 4) & 0x1000 | ((unsigned int)v16 >> 13) & 0x2000 | ((unsigned int)v17 >> 10) & 0x10000 | 4 * v17 & 0x20000 | (v17 << 10) & 0x40000 | ((unsigned int)v17 >> 1) & 0x100000 | (v17 << 9) & 0x200000 | (v17 << 6) & 0x1000000 | (v17 << 18) & 0x2080000 | (v17 << 14) & 0x8000000 | (v17 << 28) & 0x10000000 | 16 * v17 & 0x24000000 | ((unsigned int)v16 >> 24) & 1;
    *result = 4 * (_BYTE)v16 & 4 | ((unsigned int)v16 >> 3) & 0x11 | ((unsigned int)v16 >> 7) & 0x20 | ((_WORD)v16 << 7) & 0x100 | v16 & 0x200 | ((unsigned int)v16 >> 9) & 0x400 | ((unsigned int)v16 >> 14) & 0x808 | ((_WORD)v16 << 8) & 0x1000 | ((unsigned int)v16 >> 2) & 0x2000 | ((unsigned int)v17 >> 4) & 0x10000 | (v17 << 15) & 0x20000 | ((unsigned int)v17 >> 6) & 0x40000 | 8 * v17 & 0x80000 | (v17 << 11) & 0x100000 | (v17 << 16) & 0x200000 | 2 * v17 & 0x1000000 | ((unsigned int)v17 >> 2) & 0x2000000 | (v17 << 22) & 0x4000000 | (v17 << 10) & 0x8000000 | (v17 << 17) & 0x10000000 | (v17 << 15) & 0x20000000 | ((unsigned int)v16 >> 21) & 2;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__int64 __fastcall des_ecb_crypt(__int64 a1, __int64 a2, __int64 a3, signed __int64 a4, __int64 a5, int a6)
{
    v10 = (v9 ^ (v8 >> 4)) & 0xF0F0F0F ^ v9;
  v11 = 16 * ((v9 ^ (v8 >> 4)) & 0xF0F0F0F) ^ v8;
  v15 = (v14 ^ ((unsigned int)v13 >> 2)) & 0x33333333;
  v18 = (v16 ^ ((unsigned int)v17 >> 8)) & 0xFF00FF;
  v21 = (v20 ^ ((unsigned int)v19 >> 1)) & 0x55555555; //INITIAL_PERMUTATION (left, work, right);初始置换
  v28 = sbox2[(unsigned __int64)(((unsigned int)v27 >> 24) & 0x3F)] ^ sbox4[(unsigned __int64)(((unsigned int)v27 >> 16) & 0x3F)] ^ sbox6[(unsigned __int64)(((unsigned int)v27 >> 8) & 0x3F)] ^ sbox8[(unsigned __int64)(v27 & 0x3F)] ^ v23; //DES_ROUND (right, left, work, keys);16轮运算
  ...
  v149 = (v144 ^ ((unsigned int)v148 >> 1)) & 0x55555555;
  v152 = (v151 ^ ((unsigned int)v150 >> 8)) & 0xFF00FF;
  v155 = (v153 ^ ((unsigned int)v154 >> 2)) & 0x33333333;
  v160 = (v158 << 16) ^ v156;
  v161 = (v159 ^ ((unsigned int)v160 >> 4)) & 0xF0F0F0F; //FINAL_PERMUTATION(left, temp, right);终结置换
}

Triple DES:密钥长度为56*3bit=21byte,进行3次DES运算,加密过程:

  1. 以K1加密
  2. 以K2解密
  3. 以K3加密

因此,如果令K1=K3,则等效于进行了双密钥加密;如果令K1=K2=K3,则等效于进行了普通的单密钥加密(因为先K1加密,再K2=K1解密得到明文,再以K3=K1加密,等效于直接对明文进行DES加密)

AES(Advanced Encryption Standard)算法

密钥长度128bit=16byte、192bit=24byte、256bit=32byte,分别称作AES-128、AES-192、AES-256。明文以128bit=16byte为一组。密钥和明文均以字节数组unsigned char key[AES_KEYLEN]方式读入。识别方法:uint8_t sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,…

  • 密钥扩展:对128bit、192bit、256bit,借助SBox分别生成4*(10+1)=44、4*(12 + 1)=52、4*(14+1)=60个32bit的子密钥。
  • 加密过程:AES-128、AES-192、AES-256分别执行10、12、14次轮函数进行变换。轮函数由SubBytes(查表)、ShiftRows(第一行保持不变、第二行左移一个字节、第三行左移两个字节、第四行左移三个字节)、MixColumns(矩阵乘法)、AddRoundKey(与密钥异或)。最后一轮没有MixColumns。
  • 解密过程类似,使用加密的逆算法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
__int64 __fastcall KeyExpansion(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  for ( i = 0; i <= 3; ++i )
  {
    *(_BYTE *)(a4 + 4 * i) = *(_BYTE *)(4 * i + a3);
    *(_BYTE *)(a4 + 4 * i + 1) = *(_BYTE *)(4 * i + 1 + a3);
    *(_BYTE *)(a4 + 4 * i + 2) = *(_BYTE *)(4 * i + 2 + a3);
    result = *(_BYTE *)(4 * i + 3 + a3);
    *(_BYTE *)(a4 + 4 * i + 3) = result; //第一轮的key是key本身
  }
  for ( j = 4; j <= 0x2B; ++j )
  {
    v8 = *(_BYTE *)(4 * (j + 0x3FFFFFFF) + v14);
    v9 = *(_BYTE *)(4 * (j + 0x3FFFFFFF) + 1 + v14);
    v10 = *(_BYTE *)(4 * (j + 0x3FFFFFFF) + 2 + v14);
    v11 = *(_BYTE *)(4 * (j + 0x3FFFFFFF) + 3 + v14);
    if ( !(j & 3) )
    {
      v9 = sbox[v10];
      v10 = sbox[v11];
      v11 = sbox[v8];
      v8 = sbox[*(_BYTE *)(4 * (j + 0x3FFFFFFF) + 1 + v14)] ^ Rcon[j >> 2];
    }
    v5 = 4 * j;
    v6 = 4 * (j + 1073741820);
    *(_BYTE *)(v14 + 4 * j) = *(_BYTE *)(4 * (j + 1073741820) + v14) ^ v8;
    *(_BYTE *)(v14 + (unsigned int)(v5 + 1)) = *(_BYTE *)((unsigned int)(v6 + 1) + v14) ^ v9;
    *(_BYTE *)(v14 + (unsigned int)(v5 + 2)) = *(_BYTE *)((unsigned int)(v6 + 2) + v14) ^ v10;
    v7 = *(_BYTE *)((unsigned int)(v6 + 3) + v14);
    result = v7 ^ (unsigned int)v11;
    *(_BYTE *)(v14 + 4 * j + 3) = v7 ^ v11; //其他轮的key由前面轮的key和Sbox生成
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
_BYTE *__fastcall Cipher(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  AddRoundKey(a1, a2, a4, 0, a3);
  for ( i = 1; (unsigned __int8)i <= 9u; ++i )
  {
    SubBytes(a1);
    ShiftRows(a1);
    MixColumns(a1);
    AddRoundKey(a1, a2, v6, (unsigned __int8)i, v7);
  }
  SubBytes(a1);
  ShiftRows(a1);
  return AddRoundKey(a1, a2, v6, 0xAu, v7); //最后一轮没有MixColumns操作
}
例:tailbone(高校运维赛eis-2018)

Intel的aesenc指令是:

1
2
3
4
AES_SubBytes(state);
AES_ShiftRows(state);
AES_MixColums(state);
AES_AddRoundKey(state, key);

解密不能直接使用aesdec指令,而需要仿照AES解密算法:

1
2
3
4
5
def adedec(state, key):
  add_round_key(state, key)
  inv_mix_columns(state, key)
  inv_shift_rows(state, key)
  inv_sub_bytes(state, key)

因此当加密步骤为:

.eh_frame:00000000004007D8 66 0F 38 DC C2                              aesenc  xmm0, xmm2
.eh_frame:00000000004007DD 66 0F 38 DC C3                              aesenc  xmm0, xmm3
.eh_frame:00000000004007E2 66 0F 38 DC C4                              aesenc  xmm0, xmm4
.eh_frame:00000000004007E7 66 0F 38 DC C5                              aesenc  xmm0, xmm5

则需要逆序使用key进行解密:

1
2
3
4
aesdec(xmm0, xmm5)
aesdec(xmm0, xmm4)
aesdec(xmm0, xmm3)
aesdec(xmm0, xmm2)

附:Intel的aesdec指令:

1
2
3
4
AES_InvSubBytes(state);
AES_InvShiftRows(state);
AES_InvMixColums(state);
AES_AddRoundKey(state, key);

SM4国密算法(无线局域网标准的分组数据算法)

密钥长度128bit=16byte,明文以128bit=16byte为一组。密钥和明文均以字节数组unsigned char key[16]方式读入。识别方法:char Sbox[256]={0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,…

  • 初始化密钥:将key转化为4个32bit整形先于常量数组FK[4]异或,再借助Sbox生成32个32bit子密钥。
  • 加密过程与DES算法相似,每一轮中将128bit转为4个32bit的整数放入unsigned long ulbuf[4]中。迭代地使用Sbox进行非线性变换,然后再通过循环移位操作进行线性变换进行32轮加密。其每轮加密用到了之前四轮加密的结果,进一步提高了加密的强度。
  • 解密过程类似,使用逆序的子密钥。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall sm4_setkey(int a1, __int64 a2, __int64 a3, __int64 a4)
{
  v9 = v13 ^ 0xA3B1BAC6; //key转化为4个32bit整数与常量FK[4] = {0xa3b1bac6,0x56aa3350,0x677d9197,0xb27022dc}异或
  v10 = v14 ^ 0x56AA3350;
  v11 = v15 ^ 0x677D9197;
  result = v4 ^ 0xB27022DC;
  while ( v17 <= 0x1F ) //转化为32个子密钥
  {
    v6 = v17 + 4;
    v7 = *(&v9 + v17);
    v8 = (*(&v9 + v17 + 3) ^ *(&v9 + v17 + 2) ^ *(&v9 + v17 + 1)) == CK[(unsigned __int64)v17];
    *(&v9 + (unsigned int)v6) = (unsigned __int64)sm4CalciRK(a1) ^ v7; //k[i+4] = k[i] ^ (sm4CalciRK(k[i+1]^k[i+2]^k[i+3]^CK[i])); 设CKij为CKi的第j字节,ckij=(4i+j)*7(mod 256)
    result = *(&v9 + v17 + 4);
    *(_DWORD *)(v18 + 4LL * v17++) = result; //SK[i] = k[i+4];
  }
}
1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall sm4CalciRK(char a1)
{
  v1 = sm4Sbox(a1); //借助Sbox
  v2 = sm4Sbox(a1);
  v3 = sm4Sbox(a1);
  v4 = sm4Sbox(a1);
  v5 = (v3 << 8) | (v2 << 16) | (v1 << 24) | v4;
  v6 = __ROR4__(v5, 19);
  v7 = __ROR4__(v5, 9);
  return v7 ^ v5 ^ v6;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__int64 __fastcall sm4_one_round(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5)
{
  //GET_ULONG_BE( ulbuf[0], input, 0 )
  //GET_ULONG_BE( ulbuf[1], input, 4 )
  //GET_ULONG_BE( ulbuf[2], input, 8 )
  //GET_ULONG_BE( ulbuf[3], input, 12 )
  while ( v20 <= 0x1F ) // 32轮运算
  {
    *(&v12 + (unsigned int)v6) = sm4F(a1,a2,*(&v12 + v20 + 1),*(&v12 + v20),*(&v12 + v20 + 2),*(&v12 + v20 + 3),v9,v10,v11); //ulbuf[i+4] = sm4F(ulbuf[i], ulbuf[i+1], ulbuf[i+2], ulbuf[i+3], sk[i]);前4轮结果与subkey[i]借助Sbox运算
    ++v20;
  }
  //PUT_ULONG_BE(ulbuf[35],output,0);
  //PUT_ULONG_BE(ulbuf[34],output,4);
  //PUT_ULONG_BE(ulbuf[33],output,8);
  //PUT_ULONG_BE(ulbuf[32],output,12);
  return result;
}

编码算法

base32

每5byte为一组,编码成8个5bit编码的字符。因此,最后余1个byte,补6个等号;余2个byte,补4个等号;余3个byte,补3个等号;余4个byte,补1个等号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void __fastcall encode_sequence(__int64 a1, __int64 a2, signed int a3, __int64 a4, __int64 a5)
{
  for ( i = 0; i <= 7; ++i ) //编码成8个5bit编码的字符
  {
    v8 = get_octet(a1); //(block*5) / 8,当前block从哪个plain byte开始
    v7 = get_offset(a1); //(8 - 5 - (5*block) % 8),当前block还剩下多少bit
    if ( v8 >= v12 )
    {
      pad(a1, a2, 8 - i, v13 + i);
      return;
    }
    v5 = *(_BYTE *)(v8 + v11);
    v9 = shift_right(a1, a2, v7);
    if ( v7 < 0 && v12 - 1 > v8 )
    {
      v6 = *(_BYTE *)(v8 + 1LL + v11);
      v9 |= shift_right(a1, a2, (unsigned __int8)v7 + 8);
    }
    *(_BYTE *)(i + v13) = encode_char(a1, a2, i, v9); //coded[block] = encode_char(c);查表
  }
}

base64

每6byte为一组,编码成8个6bit编码的字符,也就是每3个byte即可编码成4个字符。因此,最后余1个byte,补2个等号;余2个byte,补1个等号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
__int64 __fastcall base64_encode(__int64 a1, __int64 a2, unsigned int a3, __int64 a4, __int64 a5)
{
  for ( i = 0; i < a3; ++i )
  {
    v5 = i % 3;
    if ( i % 3 == 1 )
    {
      *(_BYTE *)(v7 + a5) = *(&base64en[16 * (*(_BYTE *)(i - 1 + a4) & 3)] + ((*(_BYTE *)(i + a4) >> 4) & 0xF)); //out[j++] = base64en[((in[i-1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)];
    }
    else if ( v5 == 2 )
    {
      *(_BYTE *)(v12 + a5) = *(&base64en[4 * (*(_BYTE *)(i - 1 + a4) & 0xF)] + ((*(_BYTE *)(i + a4) >> 6) & 3)); //out[j++] = base64en[((in[i-1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)];
      *(_BYTE *)((unsigned int)v8 + a5) = base64en[*(_BYTE *)(i + a4) & 0x3F]; //out[j++] = base64en[in[i] & 0x3F];
    }
    else if ( !v5 )
    {
      *(_BYTE *)(v6 + a5) = base64en[(*(_BYTE *)(i + a4) >> 2) & 0x3F]; //out[j++] = base64en[(in[i] >> 2) & 0x3F];
    }
  }
  v14 = i - 1;
  if ( v14 % 3 )
  {
    if ( v14 % 3 == 1 )
    {
      *(_BYTE *)(v12 + a5) = base64en[4 * (*(_BYTE *)(v14 + a4) & 0xF)]; //out[j++] = base64en[(in[i] & 0xF) << 2];
      *(_BYTE *)(v12 + 1 + a5) = '=';
    }
  }
  else
  {
    v9 = v12;
    v10 = v12 + 1;
    *(_BYTE *)(v9 + a5) = base64en[16 * (*(_BYTE *)(v14 + a4) & 3)]; //out[j++] = base64en[(in[i] & 0x3) << 4];
    *(_BYTE *)((unsigned int)v10 + a5) = '=';
    *(_BYTE *)((unsigned int)(v10 + 1) + a5) = '=';
  }
}

此外,还有base16(将每个字节按hex输出),base24,base60。