为了水博客不择手段(其实是ida自动绕反调试的插件没安上做不动NCTF了,呜呜呜)
tea已经水过了,那就水一篇rc4吧(
简介
RC4(来自Rivest Cipher 4的缩写)是一种流加密算法,密钥长度可变。它的加解密使用相同的密钥,因此也属于对称加密算法。RC4是有线等效加密(WEP)中采用的加密算法,也曾经是TLS可采用的算法之一。其最重要的实现是初始化算法和伪随机子密码的生成。
RC4的实现是以字节流的方式依次加密明文中的每一个字节,解密的时候也是依次对密文中的每个字节解密。(只有异或操作和S盒,所以加解密的过程相同,这是其可逆性的原理)
算法实现
先贴一份c++实现的代码
void init(int *s, char *key, int len)
{
int t[256] = {0};
char tmp = 0;
for (int i = 0; i < 256; ++i)
{
s[i] = i;
t[i] = key[i % len];
}
int j = 0;
for (int i = 0; i < 256; ++i)
{
j = (j + s[i] + t[i]) % 256;
swap(s[i], s[j]);
}
}
void crypt(int *s, char *data, int len)
{
int i = 0, j = 0, t = 0;
char tmp;
for (int k = 0; k < len; ++k)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
swap(s[i], s[j]);
t = (s[i] + s[j]) % 256;
data[k] ^= s[t];
}
}
初始化算法(KSA)
先来看看初始化算法,此处我们假设S盒的长度是256,密钥key的长度为len且key的内容由用户规定,其长度的可变范围不限(超过256的不要就行)
void init(int *s, char *key, int len)
{
int t[256] = {0};
char tmp = 0;
for (int i = 0; i < 256; ++i)
{
s[i] = i;
t[i] = key[i % len];
}
int j = 0;
for (int i = 0; i < 256; ++i)
{
j = (j + s[i] + t[i]) % 256;
swap(s[i], s[j]);
}
}
初始化算法的步骤为:
- 初始化S盒为0-255
- 扩展密钥长度,具体操作是:若key的长度小于256,则不断将其复制进临时数组t,直到t的长度刚好达到256(多的就不要了)
- 根据t数组打乱S盒,使S盒近似为一个随机生成的、内含元素只有0-255的数组
在该算法中,i确保了S盒的每个元素都被处理过,而j确保了这种打乱的处理是近似于随机的
伪随机子密码生成算法(PRGA)
void crypt(int *s, char *data, int len)
{
int i = 0, j = 0, t = 0;
char tmp;
for (int k = 0; k < len; ++k)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
swap(s[i], s[j]);
t = (s[i] + s[j]) % 256;
data[k] ^= s[t];
}
}
该算法对data的每一位都进行了加密,具体操作是通过一定的算法找到了S盒中的某一元素,并与data当前字节异或,得到密文。
值得注意的是,data保存为明文时,加密后得到密文;data保存为密文时,加密后得到明文。
实现
#include <bits/stdc++.h>
using namespace std;
void init(int *s, char *key, int len)
{
int t[256] = {0};
char tmp = 0;
for (int i = 0; i < 256; ++i)
{
s[i] = i;
t[i] = key[i % len];
}
int j = 0;
for (int i = 0; i < 256; ++i)
{
j = (j + s[i] + t[i]) % 256;
swap(s[i], s[j]);
}
}
void crypt(int *s, char *data, int len)
{
int i = 0, j = 0, t = 0;
char tmp;
for (int k = 0; k < len; ++k)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
swap(s[i], s[j]);
t = (s[i] + s[j]) % 256;
data[k] ^= s[t];
}
}
char data[2333] = "flag{this_is_a_sample_flag}";
char key[2333] = "Hello_RC4";
int s[260];
int main()
{
// freopen("test.data", "r", stdin);
// freopen("test.data", "w", stdout);
// cin >> data;
int len = strlen(key);
init(s, key, len);
len = strlen(data);
crypt(s, data, len);
cout << data;
return 0;
}
当我们执行加密时,取消代码中stdout的注释,可以看到输出为
然后我们执行解密,取消代码中stdin和cin的注释,可以看到输出为
显然是乱码没读入好导致的(
一个在网上嫖的python实现的轮子
从上面的实现可以看到c++实现的局限性:遇到这种乱码可能解密不全
所以还是用hex值来加解密好一些,乱码什么的搞出来可能还得爆破一次(
为什么我不自己写呢?因为我不会python呜呜呜
# -- coding: utf-8 --
class RC4:
def __init__(self, k):
self.Sbox = self.RC4_init(k) # 构造生成S盒
def RC4_init(self, k):
Sbox = [] * 255
T = [] * 256
for i in range(256):
Sbox.append(i) # 初始化 Sbox放入0-255数
T.append(ord(k[i % len(k)])) # 存放轮转的256位key
j = 0
for i in range(256):
j = (j + Sbox[i] + T[i]) % 256
Sbox[i], Sbox[j] = Sbox[j], Sbox[i]
return Sbox
def RC4_crypt(self, m):
c = ''
i = j = 0
S = self.Sbox
for n in range(len(m)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i] # 交换 更新Sbox
t = (S[i] + S[j]) % 256
c += '%02x' % (ord(m[n]) ^ S[t]) # keystream
return c
# 加密过程
key = 'Hello_RC4'
rc4 = RC4(key)
# data='5bfe81e7151b1bb2d99eb9571c1aa73121c93215ae7f7b4c8dd944'.decode('hex')
# data = 'flag{this_is_a_sample_flag}'
data = 'flag{this_is_a_sample_flag}'
c = rc4.RC4_crypt(data)
print(c)
# 解密过程
key = 'Hello_RC4'
rc4 = RC4(key)
c = rc4.RC4_crypt(c.decode('hex'))
print(c.decode('hex'))
# 记一下:这轮子是py2环境的,记得用在线ide跑
运行结果:(第一行是密文的hex值)
例题:[DNUICTF 2021] Remember Crypt 4
这题名字提醒的很明显了:是个RC4的题,现在刚好拿出来复现一下
首先观察函数sub_140001120
刚学完RC4的我们显然的发现这是RC4的KSA操作其中,v9是S盒,并且这里不存在临时数组t,而是由v6充当i % len(a3就是len),在第二次循环里现算现用。
然后是函数sub_140001240
也很显然是RC4的PRGA操作,不再赘述
最后,加密后的v10与0x22异或后与byte_14013B000数组相等,key是12345678abcdefghijklmnopqrspxyz,拿出byte数组的值,异或了后扔进C++版的RC4解密,输出就得到了flag:flag{nice_to_meet_you}