文章总结: 本文详解RC4流加密算法原理、C代码实现及逆向识别特征,涵盖标准与魔改RC4的区别。结合CTF实战案例,演示了静态分析密钥、动态调试提取密文及编写脚本逆向还原的方法,为破解RC4及其变种提供了具体可操作的技术路径。 综合评分: 91 文章分类: 逆向分析,CTF,二进制安全
RC4算法逆向分析
南行
看雪学苑
2025年12月25日 17:59 上海
在密码学中,RC4 是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。RC4 是有线等效加密(WEP)中采用的加密算法,也曾经是 TLS 可采用的算法之一。
#
流密码也属于对称密码,但与分组加密算法不同的是,流密码不对明文数据进行分组,而是用密钥生成与明文一样长短的密码流对明文进行加密,加解密使用相同的密钥。也就是说,RC4不是对明文进行分组处理,而是字节流的方式依次加密明文中的每一个字节。
一.加解密原理
RC4 由伪随机数生成器和异或运算组成(由于异或运算的对合性,RC4 加密解密使用同一套算法)。RC4 的密钥长度可变,范围是1~255。给定一个密钥,伪随机数生成器接受密钥并产生一个 S 盒。S 盒用来加密数据,而且在加密过程中 S 盒会变化。
#
二、加密流程
1.先初始化状态向量S(256 个字节,用来作为密钥生成的种子)按照升序,给每个字节赋值。
2。初始密钥(由用户输入),长度任意,如果输入长度小于 256 个字节,则进行轮转,直到填满。例如填入密钥的是1 2 3 4 5 ,那么填入的是1,2,3,4,5,1,3,4,5,1,2,3,4,5 由上述轮转过程得到 256 个字节的向量T(用来作为密钥流生成的种子)
3.最后是根据向量S和T生成T生成密钥流与明文进行加密
- 伪代码:
//1.初始化S和T
for(int i=0;i<255;i++){
S[i]=i;
T[i]=K[i%keylen]
}
//2.初始排列S
j=0;
for(int i=0;i<255;i++){
j=(j+S[i]+T[i])%256;
swap(S[i],S[j]); //交换S[i]和S[j]
}
//3.产生密钥流,利用密钥流和明文进行加密
i,j=0;
for(int r=0;r<len;r++){
i=(i+1)%256;
j=(j+S[i])%256;
swap(S[i],S[j]);//swap交换
t=(S[i]+S[j])%256;
k[r]=S[t];
data[r]^=k[r];
}
#
三、C代码表示
几个基本变量:
-
S-Box也就是所谓的
S盒,是一个 256 长度的char型数组,每个单元都是一个字节,算法运行的任何时候,S都包括 0-255 的 8 比特数的排列组合,只不过值的位置发生了变换。 -
密钥
char key[256]密钥的长度keylen与明文长度、密钥流的长度没有必然关系。 -
临时向量
k长度也为 256,每个单元也是一个字节。如果密钥的长度是 256 字节,就直接把密钥的值赋给k,否则,轮转地将密钥的每个字节赋给k。
初始化部分
- 参数 1 是一个 256 长度的
char型数组,定义为:unsigned char sBox[256] - 参数 2 是密钥,其内容可以随便定义:
char key[256] - 参数 3 是密钥的长度,
Len=strlen(key)
/*初始化函数*/
voidrc4_init(unsignedchar*s,unsignedchar*key, unsignedlong Len){
int i=0,j=0;
char k[256]={0};
unsignedchar tmp=0;
for(i=0;i<256;i++) {
s[i]=i; //0-255赋值给s
k[i]=key[i%Len]; //将k重新计算
}
for(i=0;i<256;i++) { //对s进行初始置换
j=(j+s[i]+k[i])%256; //给j赋值
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的搅乱是随机的。
加密部分
- 参数1 是上边
rc4_init函数中,被搅乱的S-box - 参数2 是需要加密的数据
data - 参数3 是
data的长度
/*加解密*/
voidrc4_crypt(unsignedchar*s,unsignedchar*data,unsignedlong len){
int i=0,j=0,t=0;
unsignedlong k=0;
unsignedchar tmp;
for(k=0;k<len;k++)
{
i=(i+1)%256; //固定方式生成的i
j=(j+s[i])%256; //固定方式生成的j
tmp=s[i];
s[i]=s[j]; //交换s[x]和s[y],第二次置换
s[j]=tmp;
t=(s[i]+s[j])%256; //固定方式生成的t
data[k]^=s[t]; //异或运算
}
}
每收到一个字节,就进行循环。通过一定的算法定位S-box中的一个元素,并与输入字节异或,得到k。循环中还改变了S-box。如果输入的是明文,输出的就是密文;如果输入的是密文,输出的就是明文。
主函数
#include<stdio.h>
#include<string.h>
intmain() {
unsignedchar s[256] = {0};
char key[256] = {"hello_ctfer"};
unsignedchar data[] = "hello_world";
unsignedlong key_len = strlen(key);
// 初始化s盒
rc4_init(s, (unsignedchar*)key, key_len);
// 解密数据
rc4_crypt(s, (unsignedchar*)data, 42);
// 打印解密后的数据
for (int i = 0; i < 42; i++) {
printf("%02x", (data[i]) & 0xff);
}
return 0;
}
#
四、逆向分析
特征识别
RC4 算法主要通过加密算法特征来进行识别。
- 反编译后可以看见多个循环次数为 256 的循环,以及存在一些
%256的运算。 - 会根据密钥打乱
S盒 - 最后处理输入数据的是异或。
魔改RC4
RC4 常见的魔改方法:
- 魔改初始化算法,可以将
S盒初始化值并不设置成 0-255,也可以设置成其它的,也可以在S的初始置换过程中添加可逆运算。 - 由于最后加密 flag 是利用密钥流来单字节加密的,也有人会在这个地方添加一些可逆运算来进行魔改。
例:
总之算法需要遵循对称加密性质:
enc_data=RC4(flag)flag=RC4(enc_data)
#
五、例题
标准RC4
2024Moectf RC4
题目存在花指令,我们先进行去除。去除后我们分析代码。
根据题目中的RC4字样我们就可以判断程序为 RC4 算法。
ida 反编译程序直接看到了一串包含RC4字样疑似密钥的字符,可以猜测这段字符就是密钥。
然后我们找密文尝试解密,上面的三个字符串都是一样的,我们可以尝试一下它们是不是密文。
假定程序是一个标准 RC4,编写解密脚本,然后成功解密。
- exp
from Crypto.Cipher import ARC4
key = b"RC4_1s_4w3s0m3"
enc = bytes.fromhex("A7 1A 68 EC D8 27 11 CC 8C 9B 16 15 5C D2 67 3E 82 AD CE 75 D4 BC 57 56 C2 8A 52 B8 6B D6 CC F8 A4 BA 72 2F E0 57 15 B9 24 11")
rc4 = ARC4.new(key)
dec = rc4.decrypt(enc)
print(dec)
#moectf{why_Rc4_haS_The_Rev32sabl3_pr0ceSS}
- exp
写加密脚本最好还是用 python,一个是快,另一个是可以避免类型错误。
动态调试解RC4
2024极客大挑战 让我康康你的调试
- 分析
分析代码,程序第输入进行了异或加密,然后将加密后的输入内容作为参数传入了sub_14A6,接下来我们跟进分析。
很明显就可以看出来这是一个 rc4 加密算法,我们这里通过动态调试的方法来解这道题。
- exp
首先我们将程序动态调试起来,然后输入一段符号输入要求长度的测试数据。
本题代码中输入要求为 33 字节,我们构造一个 33 字节的字符串,将程序动态调试起来然后输入。
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
输入完成后,我们让程序运行到执行完加密逻辑后的代码部分。
然后我们提取加密后的密文,即v5[9]。
这里我们可以通过插件 Lazyida 快速提取。
然后我们再次动态调试程序将 33 个a输入程序来占位,然后我们在加密逻辑之前下断点让运行到这里停下。
之后使用我们的 Lazyida 插件的 Paste data 功能将提取出的密文粘贴到占位的a中,直接选中头字符即可。
我们让程序走过加密逻辑后再次查看密文,可以看到已经变成了我们最初输入的明文a。这里我们已经可以判断程序的加解密是对称的,也就意味着我们只要把程序的密文输入到程序中经过加密逻辑解密就会被解密成明文。
我们可以猜测这这个被数据初始化的数组就是我们的密文。
我们动态调试运行然后通过 lazyida 提取初始化后的密文数据。
然后我们和上面的逻辑一样将程序输入足够的字符占位,然后再把密文复制进去之后再运行程序解密。
运行之后可以看到我们已经成功解出了 flag。
魔改RC4
YLCTF xorplus
- 分析
ida 反编译,从函数名称就可以判断为 rc4 加密,接下来我们寻找密钥。
在主函数中找到密钥,然后分析 rc4 算法是否是标准 rc4。
分析发现在 rc4 初始化部分存在魔改,即v8 = (v8 + *(k + a1) + v9[k] + 1300) % 256;。
接下来分析一下加密部分。
算法加密部分同样存在魔改,*(a2 + i) = (*(v8 + a1) ^ *(a2 + i)) + 20;。
最后我们获取平台给的密文,然后根据魔改后的加密代码逻辑编写解密代码。
- exp
enc=[0x91,0x86,0x1b,0x2d,0x9e,0x6f,0x58,0x31,0x46,0xf0,0xed,0xa2,0xcc,0x90,0x22,0x15,0x8d,0xa2,0x61,0x2d,0x80,0x5a,0x74,0x16,0x6c,0x75,0x81,0x46,0x7e,0x26,0xb5,0x9f,0x85,0x76,0x5d,0xfe,0xb7,0x52,0x54,0xc8,0x4,0x35,0xa6]
s=[0]*256
key="welcometoylctf"
for i in range(256):
s[i]=i
v6 = 0
for j in range(256):
v6=(ord(key[j%len(key)])+v6+s[j] + 1300)%256
s[j],s[v6]=s[v6],s[j]
v7 = 0
v8 = 0
for k in range(len(enc)):
v8 = (v8 + 1) % 256
v7 = (v7 + s[v8]) % 256
s[v8],s[v7]=s[v7],s[v8]
enc[k] = (enc[k] - 20) & 0xff
enc[k] ^= s[(s[v7] + s[v8]) % 256]
print(bytes(enc))
#
看雪ID:南行
https://bbs.kanxue.com/user-home-996690.htm
*本文为看雪论坛优秀文章,由 南行 原创,转载请注明来自看雪社区
往期推荐
从ANGR-CTF项目入手ANGR和符号执行技术
AI时代-逆向工作者该如何用好这一利器
EXIF解析缓冲区溢出漏洞分析与利用
从C到Pwn:栈溢出漏洞利用实战入门
Android-ARM64的VMP分析和还原
球分享
球点赞
球在看
点击阅读原文查看更多
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:看雪学苑 南行《RC4算法逆向分析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论