RC4加密算法与逆向方法简析
参考文章:
https://www.cnblogs.com/zibility/p/5404478.html
https://www.52pojie.cn/thread-800115-1-1.html
https://blog.csdn.net/mcmuyanga/article/details/109856288
算法分析
基本介绍
RC4 是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。RC4 是有线等效加密(WEP)中采用的加密算法,也曾经是 TLS 可采用的算法之一。
加解密原理
RC4 由伪随机数生成器和异或运算组成(由于异或运算的对合性,RC4 加密解密使用同一套算法)。RC4 的密钥长度可变,范围是[1,255]。RC4 一个字节一个字节地加解密。给定一个密钥,伪随机数生成器接受密钥并产生一个 S 盒。S 盒用来加密数据,而且在加密过程中 S 盒会变化。
C代码表示
几个基本变量:
- S-Box 也就是所谓的 S 盒,是一个 256 长度的 char 型数组,每个单元都是一个字节,算法运行的任何时候,S 都包括 0-255 的 8 比特数的排列组合,只不过值的位置发生了变换。
- 密钥K char key[256] 密钥的长度 keylen 与明文长度、密钥流的长度没有必然关系
- 临时向量 k 长度也为 256,每个单元也是一个字节。如果密钥的长度是 256 字节,就直接把密钥的值赋给 k,否则,轮转地将密钥的每个字节赋给 k
初始化部分
参数 1 是一个 256 长度的 char 型数组,定义为: unsigned char sBox[256]
参数 2 是密钥,其内容可以随便定义:char key[256]
参数 3 是密钥的长度,Len = strlen(key)
/*初始化函数*/
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;
}
}
初始化长度为 256 的 S 盒。第一个 for 循环将 0 到 255 的互不重复的元素装入 S 盒。第二个 for 循环根据密钥打乱 S 盒,i 确保 S-box 的每个元素都得到处理,j 保证 S-box 的搅乱是随机的。而不同的 S-box 在经过伪随机子密码生成算法的处理后可以得到不同的子密钥序列,将 S-box 和明文进行 xor 运算,得到密文,解密过程也完全相同。
加密部分
参数 1 是上边 rc4_init 函数中,被搅乱的 S-box
参数 2 是需要加密的数据 data
参数 3 是 data 的长度
/*加解密*/
void rc4_crypt(unsigned char*s,unsigned char*Data,unsigned long Len)
{
int i=0,j=0,t=0;
unsigned long k=0;
unsigned char tmp;
for(k=0;k<Len;k++)
{
i=(i+1)%256;
j=(j+s[i])%256;
tmp=s[i];
s[i]=s[j];//交换s[x]和s[y]
s[j]=tmp;
t=(s[i]+s[j])%256;
Data[k]^=s[t];
}
}
每收到一个字节,就进行循环。通过一定的算法定位 S 盒中的一个元素,并与输入字节异或,得到 k。循环中还改变了 S 盒。如果输入的是明文,输出的就是密文;如果输入的是密文,输出的就是明文。
主函数
int main()
{
unsigned char s[256] = { 0 }, s2[256] = { 0 };//S-box
char key[256] = { "justfortest" };
char pData[512] = "这是一个用来加密的数据Data";
unsigned long len = strlen(pData);
int i;
printf("pData=%s\n", pData);
printf("key=%s,length=%d\n\n", key, strlen(key));
rc4_init(s, (unsigned char*)key, strlen(key)); //已经完成了初始化
printf("完成对S[i]的初始化,如下:\n\n");
for (i = 0; i<256; i++)
{
printf("%02X", s[i]);
if (i && (i + 1) % 16 == 0)putchar('\n');
}
printf("\n\n");
for (i = 0; i<256; i++) //用s2[i]暂时保留经过初始化的s[i],很重要的!!!
{
s2[i] = s[i];
}
printf("已经初始化,现在加密:\n\n");
rc4_crypt(s, (unsigned char*)pData, len);//加密
printf("pData=%s\n\n", pData);
printf("已经加密,现在解密:\n\n");
//rc4_init(s,(unsignedchar*)key,strlen(key));//初始化密钥
rc4_crypt(s2, (unsigned char*)pData, len);//解密
printf("pData=%s\n\n", pData);
return 0;
}
逆向分析
无壳,IDA 载入,查看 main 函数
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 result; // rax
int i; // [rsp+Ch] [rbp-E4h]
char v5[16]; // [rsp+10h] [rbp-E0h] BYREF
char s[64]; // [rsp+20h] [rbp-D0h] BYREF
char v7[64]; // [rsp+60h] [rbp-90h] BYREF
char v8[72]; // [rsp+A0h] [rbp-50h] BYREF
unsigned __int64 v9; // [rsp+E8h] [rbp-8h]
v9 = __readfsqword(0x28u);
memset(v8, 0, 0x40uLL);
v8[0] = 0xC6;
v8[1] = 0x21;
v8[2] = 0xCA;
v8[3] = 0xBF;
v8[4] = 0x51;
v8[5] = 0x43;
v8[6] = 0x37;
v8[7] = 0x31;
v8[8] = 0x75;
v8[9] = 0xE4;
v8[0xA] = 0x8E;
v8[0xB] = 0xC0;
v8[0xC] = 0x54;
v8[0xD] = 0x6F;
v8[0xE] = 0x8F;
v8[0xF] = 0xEE;
v8[0x10] = 0xF8;
v8[0x11] = 0x5A;
v8[0x12] = 0xA2;
v8[0x13] = 0xC1;
v8[0x14] = 0xEB;
v8[0x15] = 0xA5;
v8[0x16] = 0x34;
v8[0x17] = 0x6D;
v8[0x18] = 0x71;
v8[0x19] = 0x55;
v8[0x1A] = 8;
v8[0x1B] = 7;
v8[0x1C] = 0xB2;
v8[0x1D] = 0xA8;
v8[0x1E] = 0x2F;
v8[0x1F] = 0xF4;
v8[0x20] = 0x51;
v8[0x21] = 0x8E;
v8[0x22] = 0xC;
v8[0x23] = 0xCC;
qmemcpy(&v8[0x24], "3S1", 3);
v8[0x28] = 0x40;
v8[0x29] = 0xD6;
v8[0x2A] = 0xCA;
v8[0x2B] = 0xEC;
v8[0x2C] = 0xD4;
puts("Input flag: ");
__isoc99_scanf("%63s", s);
if ( strlen(s) == 0x2D )
{
strcpy(v5, "Nu1Lctf233");
sub_400874(v5, s, v7);
for ( i = 0; i <= 0x2C; ++i )
{
if ( v7[i] != v8[i] )
{
puts("GG!");
return 0LL;
}
}
puts("Congratulations!");
result = 0LL;
}
else
{
puts("GG!");
result = 0LL;
}
return result;
}
sub_400874 如下,sub_40067A()和 sub_400753()两个函数都是加密算法
sub_40067A()是流密钥的生成
sub_400753()是加密
其中 sub_400646 函数的作用就相当于 swap
本题的密钥是 Nu1Lctf233
加密后的密文是 v8,也就是
[0xc6,0x21,0xca,0xbf,0x51,0x43,0x37,0x31,0x75,0xe4,0x8e,0xc0,0x54,0x6f,0x8f,0xee,0xf8,0x5a,0xa2,0xc1,0xeb,0xa5,0x34,0x6d,0x71,0x55,0x8,0x7,0xb2,0xa8,0x2f,0xf4,0x51,0x8e,0xc,0xcc,0x33,0x53,0x31,0x0,0x40,0xd6,0xca,0xec,0xd4 ]
对它进行 utf-8 编码得到真正的密文
import base64
a=[0xc6,0x21,0xca,0xbf,0x51,0x43,0x37,0x31,0x75,0xe4,0x8e,0xc0,0x54,0x6f,0x8f,0xee,0xf8,0x5a,0xa2,0xc1,0xeb,0xa5,0x34,0x6d,0x71,0x55,0x8,0x7,0xb2,0xa8,0x2f,0xf4,0x51,0x8e,0xc,0xcc,0x33,0x53,0x31,0x0,0x40,0xd6,0xca,0xec,0xd4]
s=""
for i in a:
s+=chr(i)
print(s)
print(str(base64.b64encode(s.encode('utf-8')), 'utf-8'))
密文:w4Yhw4rCv1FDNzF1w6TCjsOAVG/Cj8Ouw7hawqLDgcOrwqU0bXFVCAfCssKoL8O0UcKODMOMM1MxAEDDlsOKw6zDlA==
贴上百度的脚本,拿到 flag
import base64
def rc4_main(key = "init_key", message = "init_message"):
print("RC4解密主函数调用成功")
print('\n')
s_box = rc4_init_sbox(key)
crypt = rc4_excrypt(message, s_box)
return crypt
def rc4_init_sbox(key):
s_box = list(range(256))
print("原来的 s 盒:%s" % s_box)
print('\n')
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
print("混乱后的 s 盒:%s"% s_box)
print('\n')
return s_box
def rc4_excrypt(plain, box):
print("调用解密程序成功。")
print('\n')
plain = base64.b64decode(plain.encode('utf-8'))
plain = bytes.decode(plain)
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k))
print("res用于解密字符串,解密后是:%res" %res)
print('\n')
cipher = "".join(res)
print("解密后的字符串是:%s" %cipher)
print('\n')
print("解密后的输出(没经过任何编码):")
print('\n')
return cipher
a=[0xc6,0x21,0xca,0xbf,0x51,0x43,0x37,0x31,0x75,0xe4,0x8e,0xc0,0x54,0x6f,0x8f,0xee,0xf8,0x5a,0xa2,0xc1,0xeb,0xa5,0x34,0x6d,0x71,0x55,0x8,0x7,0xb2,0xa8,0x2f,0xf4,0x51,0x8e,0xc,0xcc,0x33,0x53,0x31,0x0,0x40,0xd6,0xca,0xec,0xd4]
s=""
for i in a:
s+=chr(i)
s=str(base64.b64encode(s.encode('utf-8')), 'utf-8')
rc4_main("Nu1Lctf233", s)