常见加密算法
简单位运算
简单加密
基础加密算法 | 简易公式 | 说明 |
---|---|---|
凯撒加密 | 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值 0x9e3779b9 或 0x61c88647
- 每轮的加密特征,左移 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 索引表进行了修改。
这个是对加密结果进行了字母大小写互换。
以上操作均为可逆的,直接写脚本还原即可。