在我们做CTF逆向题目的时候,数据结构可谓是最基础的东西了,有很多题目都是逆向算法题目,所以好的算法能力对我们打CTF比赛还是很有帮助的,今天就来带领大家了解一下加密算法RC4,TEA和Base 64。
在后面的文章中,会专门出一片文章,教大家密码算法特征识别,以及变种密码算法分析。
一.RC4加密算法
1. RC4加密算法简介
RC4是一种对称的加密算法,所谓的对称加密算法是指加密和解密使用相同密钥的加密算法,数据发送方利用密钥将明文经过特殊加密算法处理后,使其变成复杂的加密密文发送出去,接收方收到密文后,若想解读原文,则需要使用加密用过的密钥以及相同的加密算法对明文进行解密,才能使其恢复成可读的明文。
RC4算法简单,运行速度快,而且密钥长度是可变的,可变范围为1-256字节(8-2048比特),生成的密钥流的长度和铭文的长度是对应的。在现在技术支持的前提下,当密钥长度为128比特是,用暴力法搜索密钥可能已经不太可行,所以能够预见RC4的密钥范围仍然能够在今后相当长的时间里低于暴力搜索哟密钥的攻击。实际上,现在也没有找到对于128bit密钥长度的RC4加密算法的有效的攻击方法。
RC4主要包括了初始化算法(KSA)和加密算法两大部分。
2. 加密过程概述
-
- 先初始化状态向量S(256字节,用来作为密钥流生成的种子1),按照升序,给每个字节赋值,0,1,2,3,4,5,6…
-
- 初始密钥(由用户输入),长度任意,如果输入长度小于256字节,则进行轮转,直到填满,例如:
输入的密钥是123456,那么填入的就是123456123456123456…
由上述轮转过程得到的256字节的向量T(用来作为密钥流生成的种子2)。
- 初始密钥(由用户输入),长度任意,如果输入长度小于256字节,则进行轮转,直到填满,例如:
-
- 最后是根据向量S和T生成密钥流,与明文进行加密。
3. 代码通常的结构:
-
- 初始化S和T:
for i=0 to do
S[i]=i;
T[i]=K[i mod keylen];
-
- 初始排列S:
j=0;
for i=0 to 255 do
j = (j+S[i]+T[i]mod256;
swap(S[i],S[j];
- 3.生成密钥流,利用密钥流和明文进行加密:
i,j=0;
for r=0 to len do //r为明文长度,r字节
i=(i+1)mod 256;
j=(j+S[i])mod 256;
swap(S[i],S[j]);
t=(S[i]+S[j])mod 256;
K[r]=S[t];
data[r]^=K[r];
4. 加解密程序源码
#include <stdio.h>
#include <string.h>
void rc4_init(unsigned char* s,char* key,unsigned long len);
void rc4_crypt(unsigned char* s,char* data,unsigned long len);
int main(){
unsigned char s[256]={0};
char key[256]={0};
char data[256]={0};
printf("请输入密钥:");
scanf("%s",key);
unsigned long length = strlen(key);
rc4_init(s,key,length);
printf("请输入密文:");
scanf("%s",data);
rc4_crypt(s,data,length);
printf("\n加/解密结果:\n%s",data);
return 0;
}
void rc4_init(unsigned char* s,char* key,unsigned long len){
int i = 0;
int j = 0;
unsigned char k[256]={0};
unsigned char temp =0;
for(i = 0;i < 256;i++){
s[i]=i; //升序初始化S向量
k[i]=key[i%len]; //用key生成 ,实际上就是T向量
}
for(i = 0;i < 256;i++){
j=(j+s[i]+k[i])%256;
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
void rc4_crypt(unsigned char* s,char* data,unsigned long len){
int i = 0, j = 0,t = 0;
unsigned long k=0;
unsigned char temp;
for(k=0;k<len;k++){
i=(i+1)%256;
j=(j+s[i])%256;
temp = s[i];
s[i]=s[j];
s[j]=temp;
t=(s[i]+s[j])%256;
data[k]^=s[t];
}
}
5.RC4算法特征识别
在我们你想的时候,可以根据特征来识别加密算法,而RC4加密算法的特征也是比较明显的:
-
- 有很多取模操作
-
- 有很多256
-
- 有多个次数为256的循环
-
- 最后操作为异或
6. RC4加解密算法魔改
RC4加解密算法的魔改可以分为一下几种:
-
- 将S盒初始值不设置为1-256
-
- 最后做异或的时候做一些可逆操作
二.TEA加密算法
1. TEA加密算法简介
TEA是Tiny Encryption Algorithm的缩写,以加密速度快,实现简单著称。REA算法每一次可以操作64bit(8byte),采用128bit(16byte)作为key,算法采用迭代的形式,推荐的迭代轮数是64轮,最少32轮(可以改变),TEA系列算法中均使用了一个DELTA常熟,但DELTA的值对算法并无什么影响,只是为了避免不良的取值,推荐DELTA的值取为黄金分割数与232的乘积,取整后的十六进制值为0x9e3779B9(也可以改变),用于保证每一轮的加密都不相同。为解决TEA算法密钥表攻击的问题,TEA算法先后经历了几次改进,从XTEA到BLOCKTEA,直至最新的XXTEA。
- XTEA:使用与TEA相同的简单运算,但四个子密钥采取不正规的方式进行混合以组织密钥表攻击。
- Block TEA:Block TEA算法可以对32位任意整数倍长度的变量块进行加解密的操作,该算法将XTEA轮循函数一次应用于块中的每个字,并且将它附加于被应用字的邻字。
- XXTEA:XTEEA使用跟Block TEA相似的结构,但在处理块中每个字时利用了相邻字,且用拥有两个输入量的MX函数代替了XTEA轮循函数。上面提到的相邻字实际上就是数组中相邻的项。
- 只要会处理TEA,XTEA和XXTEA也是同理。
2. TEA加密流程分析
我们来看看TEA加密源码:
#include <stdio.h>
#include <stdint.h>
void encrypt (uint32_t *v,uint32_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,uint32_t *k){
uint32_t v0=v[0],v1=v[1],sum=0xC6EF3720,i; //这里的sum是0x9e3779b9*32后截取32位的结果,截取很重要。
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;
}
int main()
{
uint32_t v[2]={1,2},k[4]={2,2,3,4};
printf("加密前的数据:%u %u\n",v[0],v[1]); //%u 以十进制形式输出无符号整数
encrypt(v,k);
printf("加密后数据:%u %u\n",v[0],v[1]);
decrypt(v,k);
printf("解密后数据:%u %u\n",v[0],v[1]);
return 0;
}
XTEA加密源码:
#include<stdio.h>
#include<stdint.h>
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]){
unsigned int i;
uint32_t v0=v[0],v1=v[1],sum=0,delta=0x9E3779B9;
for(i=0;i<num_rounds;i++){
v0+=(((v1<<4)^(v1>>5))+v1)^(sum+key[sum&3]);
sum+=delta;
v1+=(((v0<<4)^(v0>>5))+v0)^(sum+key[(sum>>11)&3]);
}
v[0]=v0;v[1]=v1;
}
void decipher(unsigned int num_rounds,uint32_t v[2],uint32_t const key[4]){
unsigned int i;
uint32_t v0=v[0],v1=v[1],delta=0x9E3779B9,sum=delta*num_rounds;
for(i=0;i<num_rounds;i++){
v1-=(((v0<<4)^(v0>>5))+v0)^(sum+key[(sum>>11)&3]);
sum-=delta;
v0-=(((v1<<4)^(v1>>5))+v1)^(sum+key[sum&3]);
}
v[0]=v0;v[1]=v1;
}
int main(){
uint32_t v[2]={1,2};
uint32_t const k[4]={2,2,3,4};
unsigned int r=32; //这里是加密轮数,自己设置
printf("加密前原始数据:%u %u\n",v[0],v[1]);
encipher(r,v,k);
printf("加密后原始数据:%u %u\n",v[0],v[1]);
decipher(r,v,k);
printf("解密后原始数据:%u %u\n",v[0],v[1]);
return 0;
}
XXTEA加密算法:
#include<stdio.h>
#include<stdint.h>
#define DELTA 0x933779b9
#define MX (((z>>5^y<<2)+(y>>3^z<<4))^((sum^y)+(key[(p&3)^e]^z)))
void btea(uint32_t *v,int n,uint32_t const key[4])
{
uint32_t y,z,sum;
unsigned p,rounds,e;
if(n>1)
{
rounds=6+52/n; //这里可以说是预定义值,n=2是rounds=32
sum=0;
z=v[n-1];
do
{
sum+=DELTA;
e=(sum>>2)&3;
for(p=0;p<n-1;p++) //注意这里的p是从0~n-1
{
y=v[p+1];
z=v[p]+=MX;
}
y=v[0];
z=v[n-1]+=MX; //这里的MX中传入的p=n-1
}
while(--rounds);
}
else if(n<-1)
{
n=-n;
rounds=6+52/n;
sum=rounds*DELTA;
y=v[0];
do
{
e=(sum>>2)&3;
for(p=n-1;p>0;p--) //注意这里的p是从n-1~0,和上面是反过来的
{
z=v[p-1];
y=v[p]-=MX;
}
z=v[n-1];
y=v[0]-=MX; //这里的MX中传入的 p=0
sum-=DELTA;
}
while(--rounds);
}
}
int main()
{
uint32_t v[2]={1,2};
uint32_t const k[4]={2,2,3,4};
int n=2;
printf("加密前原始数据:%u %u\n",v[0],v[1]);
btea(v,n,k);
printf("加密后数据:%u %u\n",v[0],v[1]);
btea(v,-n,k);
printf("解密后数据:%u %u\n",v[0],v[1]);
return 0;
}
3. TEA算法特征识别
-
- delta的值:通常delta的值是0x9e3779b9
-
- TEA每轮的加密特征,左移4,异或,右移5,以及一个变量sum会迭代+=delta32次
-
- XTEA每轮的加密特征,左移4,右移5,与TE不同的是对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));
- XXTEA每轮的加密特征:
4. 常见魔改方法
-
- 修改delta的值,不再是0x9e3779b9
-
- 每一轮迭代加密中添加可逆运算
-
- 在迭代完之后,赋值回去的时候,添加可逆运算
5.TEA逆向方法
通常我们解决TEA一类的算法,都是通过提取关键数据编写脚本进行解密的
在提取了关键数据后如果反编译出来的算法不是特别标准,尽量先编写一个正向的加密算法出来,然后将我们的测试数据通过我们要你想得程序进行揭秘之后得到加密数据,检验我们编写的正向加密的算法的正确性,毕竟解密只是加密的逆运算,正向对了之后逆向肯定不成问题。
不管怎么样,首先我们要提取delta的值,这是一个4字节的常量,然后便是查看每一轮的迭代加密是否添加了可逆运算,以及最后赋值回去的时候对迭代加密后的数据是否进行了其他处理。
三. Base 64加密算法
1. Base 64算法原理
Base 64是一种将二进制数据编码成文本的编码风格
Base 64使用64个编码数据:“A,B,C,D… ,a,b,c,d,e,… ,+,/,="
大家可以把Base 64理解为一种进制转换,一个字节有256种不同的数据(8bit),一个Base64字符有64种数据(6bit),可以理解为将256进制的转化位64进制,转化完成后,映射Base64编码表,取出对应字符即可
- 将字节数据编码成Base64的形式,需要更大的空间
- 6和8的最小公倍数:24
- 46==38==24
- 4位Base64可以标识3个字节数据
- 不足三个字节怎么办?用”=“对齐
2. Base64常见变形
-
- 修改编码表
-
- 修改下标
修改编码表:将Base64的编码表替换成另外一种编码表,对于这种修改,我们只需要将表的数据替换成换来的表即可实现解密。
修改下标:将Base64的编码查表下标对于关系修改,对于这种修改,我们只需要退出下标逆计算即可。
3. Base64逆向方法
-
- Base64加密算法会有一个常量表,对应Base64码表
-
- Padding计算
-
- 查表(含有0x3F)
这篇文章就分享到这里,在本专栏下一篇文章将分享识别加密算法特征以及变种密码算法分析。