[CTF从0到1]逆向中常见的加密算法

发布于 2024-02-04  324 次阅读


常见加密算法

简单位运算

前几篇里写过。

简单加密

基础加密算法 简易公式 说明
凯撒加密 Y = X + a 任意值X经过偏移为a的移位变换为Y
仿射加密 Y = aX + b 与上面类似

Base64

Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法,主要是将输入中的每 3 字节(共 24 比特)按每 6 比特分成一组,变成 4 个小于 64 的索引值,然后通过一个索引表得到 4 个可见字符。

这种加密算法类似进制转换,一个字节有 256 种不同的数据(8bit),一个 Base64 字符有 64 种数据(6bit),可以理解为将 256 进制的转化为 64 进制,转化完成后,映射 Base64 索引表,取出对应字符即可。

索引表为一个 64 字节的字符串,如果在代码中发现引用了索引表"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",那么基本上就可以确定用了 Base64 。

若加密字符长度不足三个字节,就用 “=” 补齐。

变种的 Base64:

  • 改变了索引表,需要对索引表进行还原
  • 改变了索引对应下标关系,需要还原原来的对应关系

TEA

TEA 是一种常见的分组加密算法,密钥为 128 比特位,明文为 64 比特位,主要做了 32 轮变换,每轮变换中都涉及移位和变换。

识别方法为:

  • TEA 算法中有一个固定的常数delta值 0x9e3779b90x61c88647
  • 每轮的加密特征,左移 4 ,异或,右移 5 ,以及一个变量 sum 会迭代 +=delta 32 次
  • XTEA每轮的加密特征,左移 4 ,右移 5 ,与 TEA 不同的是对 kyy 和 sum 的处理==key[(sum>>11)&3],key[sum&3]
  • XXTEA每轮的加密特征:
    sum+=delta;e=(sum>>2&3; ((z>>5^y<<2)=(y>>3^z<<4)+((sum^y)+(key[p&3)^z));

加密过程如图:

源码如下:

void encrypt(uint32_t*v,unint32_t*k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0, i;
    uint32_t delta = 0x9e3779b9;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for(i=0;i<32;i++) {
        sum += delta;
        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
    }
    v[0] = v0;
    v[1] = v1;
}
void decrypt(uint32_t*v,unint32_t*k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;
    uint32_t delta = 0x9e3779b9;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for(i=0;i<32;i++) {
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= delta;
    }
    v[0] = v0;
    v[1] = v1;
}

常见魔改方法

  • 修改 delta 的值,使其不再是 0x9e3779b9
  • 每一轮迭代加密中添加可逆运算
  • 在迭代完之后,赋值回去时,添加可逆运算

AES

AES 也是常见的分组加密算法,加解密流程如图所示:

其中,字节替代过程是通过 S 盒完成一个字节到另一个字节的映射。S 盒和逆 S 盒具体如下:

    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
 0 63 7c 77 7b f2 6b 6f c5 30  1 67 2b fe d7 ab 76
 1 ca 82 c9 7d fa 59 47 f0 ad d4 a2 af 9c a4 72 c0
 2 b7 fd 93 26 36 3f f7 cc 34 a5 e5 f1 71 d8 31 15
 3  4 c7 23 c3 18 96  5 9a  7 12 80 e2 eb 27 b2 75
 4  9 83 2c 1a 1b 6e 5a a0 52 3b d6 b3 29 e3 2f 84
 5 53 d1  0 ed 20 fc b1 5b 6a cb be 39 4a 4c 58 cf
 6 d0 ef aa fb 43 4d 33 85 45 f9  2 7f 50 3c 9f a8
 7 51 a3 40 8f 92 9d 38 f5 bc b6 da 21 10 ff f3 d2
 8 cd  c 13 ec 5f 97 44 17 c4 a7 7e 3d 64 5d 19 73
 9 60 81 4f dc 22 2a 90 88 46 ee b8 14 de 5e  b db
 a e0 32 3a  a 49  6 24 5c c2 d3 ac 62 91 95 e4 79
 b e7 c8 37 6d 8d d5 4e a9 6c 56 f4 ea 65 7a ae  8
 c ba 78 25 2e 1c a6 b4 c6 e8 dd 74 1f 4b bd 8b 8a
 d 70 3e b5 66 48  3 f6  e 61 35 57 b9 86 c1 1d 9e
 e e1 f8 98 11 69 d9 8e 94 9b 1e 87 e9 ce 55 28 df
 f 8c a1 89  d bf e6 42 68 41 99 2d  f b0 54 bb 16
    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
 0 52  9 6a d5 30 36 a5 38 bf 40 a3 9e 81 f3 d7 fb
 1 7c e3 39 82 9b 2f ff 87 34 8e 43 44 c4 de e9 cb
 2 54 7b 94 32 a6 c2 23 3d ee 4c 95  b 42 fa c3 4e
 3  8 2e a1 66 28 d9 24 b2 76 5b a2 49 6d 8b d1 25
 4 72 f8 f6 64 86 68 98 16 d4 a4 5c cc 5d 65 b6 92
 5 6c 70 48 50 fd ed b9 da 5e 15 46 57 a7 8d 9d 84
 6 90 d8 ab  0 8c bc d3  a f7 e4 58  5 b8 b3 45  6
 7 d0 2c 1e 8f ca 3f  f  2 c1 af bd  3  1 13 8a 6b
 8 3a 91 11 41 4f 67 dc ea 97 f2 cf ce f0 b4 e6 73
 9 96 ac 74 22 e7 ad 35 85 e2 f9 37 e8 1c 75 df 6e
 a 47 f1 1a 71 1d 29 c5 89 6f b7 62  e aa 18 be 1b
 b fc 56 3e 4b c6 d2 79 20 9a db c0 fe 78 cd 5a f4
 c 1f dd a8 33 88  7 c7 31 b1 12 10 59 27 80 ec 5f
 d 60 51 7f a9 19 b5 4a  d 2d e5 7a 9f 93 c9 9c ef
 e a0 e0 3b 4d ae 2a f5 b0 c8 eb bb 3c 83 53 99 61
 f 17 2b  4 7e ba 77 d6 26 e1 69 14 63 55 21  c 7d

如果发现程序中有 S 盒或者动态生成了 S 盒,那么就可以确定采用了 AES 加密。

常见的魔改:

  • 字节代换时使用逆 S 盒进行代换
  • 对行位移进行魔改,状态矩阵的第 0 行左移 0 字节,第一行左移 1 字节,第二行左移 2 字节,第三行左移 3 字节,每一行的元素左移的字节数是可以更改的
  • 对列混合进行魔改,虽然可以,但是一般不会有人这样做,状态矩阵与固定的矩阵相乘后得到混淆后的状态矩阵,如果这个固定矩阵被更改,那么求逆向时要重新计算另外一个矩阵
  • 对中间轮数的加密顺序进行更改

RC4

RC4 加密算法是对称加密算法,通过密钥 key 和 S 盒生成密钥流,明文逐字节异或 S 盒,同时 S 盒也会发生改变。所以加密与解密使用了相同的函数和密钥 K 。RC4加密的强度主要来源于密钥的安全性,如果密钥泄露,则能直接解密出明文。属于流加密算法,包括初始化函数和加解密函数。

加密过程如下:

初始化S盒 RC4 算法的关键是初始化一个 256 字节大小的 S 盒,其中包含了 0 到 255 的所有可能值的排列组合。这个盒子可以通过使用密钥作为输入来初始化。

生成伪随机流 接下来, RC4 使用密钥来生成一个流,这个流被认为是伪随机的,因为它实际上是 S 盒中的值的排列重组。这个流的长度与明文的长度相同,可以通过重复使用 S 盒来扩展它的长度。

加密明文 最后, RC4 将明文与伪随机流进行异或运算,以便生成密文。解密过程与加密过程完全相同,只需要使用相同的密钥来重新生成相同的伪随机流,然后将其与密文进行异或运算,以便还原明文。

函数代码具体如下:

/*初始化函数*/
void rc4_init(unsigned char*s,unsigned char*key,unsigned long Len) {
    int i=0,j=0;
    //char k[256]={0};
    unsigned char tmp = 0;
    for(i=0;i<256;i++) {
        s[i]=i;
        k[i]=key[i%Len];
    }
    for(i=0;i<256;i++) {
        j=(j+s[i]+k[i])%256;
        tmp=s[i];
        s[i]=s[j];//交换s[i]和s[j]
        s[j]=tmp;
    }
}
/*加解密*/
void rc4_crypt(unsigned char*s,unsigned char*Data,unsigned Long Len) {
    int i=0,j=0,t=0;
    unsigned long k=0;
    unsigned long tmp;
    for(k=0;k<Len;k++){
        i=(i+1)%256;
        j=(j+s[i])%256;
        tmp=s[i];
        s[i]=s[j];//交换s[i]和s[j]
        s[j]=tmp;
        t=(s[i]+s[j])%256;
        Data[k]^=s[t];
    }
}

可以看出,初始化代码对字符数组 s 进行了初始化赋值,且赋值分别递增,之后又对 s 进行了 256 次变换操作。通过识别初始化代码,可以判断为 RC4 加密。

特征比较明显:

  • 有很多取模操作
  • 有很多 256
  • 有多个次数为 256 的循环
  • 最后操作为异或

由于 RC4 的加密就是生成密钥流和输入进行异或,所以通常魔改就是添加一些可逆运算:

  • 魔改初始化算法,可以将 S 盒初始化值不设置为 0~255,设置为其他的,也可以在 S 的初始置换过程添加可逆运算
  • 由于最后加密 flag 是利用密钥流来进行单字节加密的,所以可以这个地方加一些可逆运算

MD5

MD5 消息摘要算法,是一种被广泛使用的密码散列函数,可以产生一个 128 位(16字节)的散列值,用于确保信息传输的完整性和一致性。MD5 加密的函数大致如下:

MD5_CTX md5c;
MD5Init(&md5c);
MD5UpdaterString(&md5c, plain);
MD5Final(digest,&md5c);

其中,MD5Init 会初始化四个称作 MD5 链接变量的整数参数。因此如果看到这四个常数 0x67452301、0xefcdab89、0x98badcfe、0x10325476,就可以怀疑该函数是否为 MD5 算法了

MD5Init 函数代码如下:

void MD5Init (MD5_CTX *context)
/*context*/
{
    context->count[0] = context->count[1] = 0;
    /* Load magic initialization constants. */
    context->state[0] = 0x67452301;
    context->state[1] = 0xefcdab89;
    context->state[2] = 0x98badcfe;
    context->state[3] = 0x10325476;
}

总结

特征值识别

各算法使用到的特征常量如下:

但有时出题人会故意对这些常量进行修改,因此还需要通过动调等手段来对算法进行验证。

特征运算识别

当特征值不足以识别出算法时,可以通过分析程序是否使用了某些特征运算来鉴别算法,常见算法的特征运算如下:

同理,也需要经过动调或复现等确认才可下定论。

例题:[ACTF新生赛2020]usualCrypt

直接看main函数:

比较明显,就是输入内容经过 sub_401080 函数处理后,与 byte_40E0E4 比较,相同就 happy。

双击 byte_40E0E4 跟进,找到了以下内容:

比较明显的 Base64 特征。但是直接把 zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9 拿去解密会解不出来,只好回去接着看代码。

双击跟进 sub_401080 函数:

ChatGPT 分析了一下,开头有个 sub_401000 函数,输入内容经过 Base64 加密后,返回的结果还会经过 sub_401030 函数处理。而 sub_401000 和 sub_401030 和加密过程都没啥关系,可能是对明文或密文进行了再加工。分别双击跟进。



显然是对 Base64 索引表进行了修改。


这个是对加密结果进行了字母大小写互换。

以上操作均为可逆的,直接写脚本还原即可。

最后更新于 2024-02-13