CTF逆向常见加密算法总结
CTF逆向常见加密算法总结
流密码系列
只要识别出流密码,我们就可以选择动态调试获取密钥流或者直接把目标密文 patch 进去拿输出就可以了
RC4
比较常见,原理不赘述,可以看我的RC4&RSA | Matriy’s blog
RC4常见时会有一个初始化256个的S盒操作
解密代码
1 | key = list('RC4_1s_4w3s0m3') |
Salsa20
Salsa20是一种流式对称加密算法,类似于Chacha20,算法性能相比AES能够快3倍以上。
Salsa20算法通过将32 Byte的key和8 Byte的随机数nonce扩展为2^70 Byte的随机字节流,通过随机字节流和异或操作实现加解密,因此Salsa20算法中随机字节流的生成为关键所在。
Salsa20算法通过将 32 字节(或者 16 字节)的密钥 和 **8 字节的iv **扩展为伪随机密钥字节流,通过伪随机密钥字节流和异或操作实现加解密。
伪随机密钥字节流的生成其实是使用 密钥、iv、以及一些常量构成 64 字节数据,输入到核心函数中得到 64 字节的输出。
Salsa20 密钥拓展规则如下:
1 | key 为 32 字节时 |
核心
1 |
|
后面接着就是 xor 了,看了前面的实现代码,我相信这种加密的识别也并不困难
首先,构造核心函数输入时的参数,是最容易识别的。(当然也是最容易魔改的)
其次,核心函数中的标志性循环左移,以及每一位对应的位移数,不要看着复杂,其实就是 7、9、13、18,可以看到上面的代码中我把每4个分成了一组,因为在实现的时候有时候会把每四个作为一组来处理。
1 |
|
最后提醒下,Salsa20 核心函数中的 20 轮也是可以魔改的。
在线解密网站:SSL在线工具-Salsa20在线加解密-Salsa20 encryption-SSLeye官网
1 |
|
在逆向分析实战中判断Salsa20算法的可从一下几点入手:
- 初始化矩阵中出现
"expand 32-byte k"或"expand 16-byte k". - 使用 ROTL32 循环左移 7、9、13、18 位。
- 20 轮循环,且分为 “列变换” 与 “行变换” 两个阶段。
- 数据块为 64 字节,逐块生成密钥流。
ChaCha20
Chacha20流密码经常和Poly1305消息认证码结合使用,被称为ChaCha20-Poly1305,由Google公司率先在Andriod移动平台中的Chrome中代替RC4使用,由于其算法精简、安全性强、兼容性强等特点,目前Google致力于全面将其在移动端推广。
ChaCha20算法使用256位的密钥(32字节) 和一个12字节的随机数(称为nonce) 作为输入,生成一个可变长度的伪随机比特流,然后与明文进行异或运算得到密文。ChaCha20算法的特点是快速、安全、易于实现和内存友好,适用于高速网络通信和移动设备。
ChaCha20 的 密钥为 32 字节,iv 为 12 字节, 计数器为 4 字节
ChaCha20 密钥拓展规则如下:
1 | c[0:4] + key[0:32] + 计数器(4 bytes) + iv |
1 | static inline void u32t8le(uint32_t v, uint8_t p[4]) { |
RCTF2022 中的checkserver 中的加密,看密钥 64 字节,没有iv,也没有常量,看起来好像不是 ChaCha20,但是看后面,标志性的循环左移16 12 8 7,我们就可以很容易的识别出来,这是一个去除了密钥拓展的 ChaCha20.
在逆向分析中判断 ChaCha20 的常见特征:
- 常量字符串
"expand 32-byte k"出现在初始化矩阵中(可能是 ASCII 值形式)。 - 内部循环 20 轮(10 次双轮),每轮包含多次 Quarter Round。
- 使用 循环左移 16、12、8、7 位的 ROTL 操作。
- 处理的数据块大小为 64 字节。
解密代码:
python用库版:
1 |
|
cpp版原理:
1 |
|
在线解密网站在线ChaCha20加密解密工具
ZUC
祖冲之加密算法(ZUC)是我国自主设计的流密码算法,主要用于与4G网络中的加密,目前主要应用在通信领域当中。
特征有两个S盒
python
1 | from math import ceil |
TEA系列
不介绍原理了,直接看解密代码吧,不是很难
TEA
1 |
|
输入出也可以这样
1
2
3 uint32_t enc[8] = { 0x877A62A6, 0x6A55F1F3, 0xAE194847, 0xB1E643E7, 0xA94FE881, 0x9BC8A28A, 0xC4CFAA9F, 0xF1A00CA1, };
printf("L3HCTF{%.32s}\n", enc);
printf("L3HCTF{");
XTEA
1 |
|
XXTEA
python
1 | def dwords_to_bytes_little_endian(dword_array): |
1 | # Implement the recovered cipher and solve for the plaintext that encrypts to the given 'ans'. |
1 | import binascii |
cpp
1 |
|
哈希
这系列可以直接cyberchef,但是逆向时仍然需要识别它
MD5
MD5算法是消息摘要算法,也是单项散列算法,其作用是将一个任意长度的消息压缩成固定长度,该算法默认会产生一个128位的消息摘要,常用于验证消息完整性以及数字签名等。
逆向识别方式:
消息摘要初始化时,会用4个变量来辅助计算消息摘要,这些寄存器会被初始化为:
A=>01234567h B=>89abcdefh C=>fedcba98h d=>76543210h
其主要加密代码是这样的,先是初始化这四个变量,然后再更新。
1 | MD5Init(&context); |
md5数据库:md5在线解密破解,md5解密加密
1 |
|
SHA1
Sha系列算法,又叫做安全散列算法,其包括 sha-1,sha-256,sha-384,sha-512总共这四种,分别产生160/256/384/512位的散列值,该算法与MD4算法设计原理相同,但安全性更高一些。
以sha-1为例,其会产生160位消息摘要,在对消息处理之前,初始散列值H用5个32位双子进行初始化,可以通过识别这些双字压缩常数来确定是否是该算法。
h0=> 67452301h h1=>efcdab89h h2=>98badcfeh h3=>10325476h h4=>c3d2e1f0h
1 |
|
CRC32
1 |
|
分组加密算法
DES
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的分组算法。
图一中的IP和FP分别代表初始置换和末尾置换,图二中的S1到S8是8个置换盒,这些都可以作为识别DES算法的特征。
解密解法
随便在GitHub上扒一份源码,就能找到这些常量:
在2020祥云杯的某道APK逆向里,Findcrypt插件失效(可能是Findcrypt分析不了ARM框架下的文件),所以我们只能靠手动分析找到DES的特征(以下是S1到S8):
1 |
|
以另一道题为例,可以讲一下如何动调解密
DES的解法还有动调patch解法,因为DES的加密因为这是 Feistel 结构:加密和解密用的是同一个轮函数,唯一的区别就是子密钥的使用顺序相反。
sub_140001CC0的核心轮逻辑是:
1 | // 每一轮 |
这就是标准 Feistel 轮,因为是异或操作,当然是直接可逆的,想一下RC4
假设只有 2 轮,子密钥是:
1 | K0, K1 |
加密
1 | (L0, R0) 明文 |
也就是:
第1轮(密钥 K0):
1 | L1' = R2 |
第二轮(密钥 K1):
加密第 2 轮
1 | 当前状态: (L1, R1) |
所以是:
1 | F(R1, K1) |
把加密里的表达式代进去会发现:
现在解密,我们把密钥反过来
Feistel 轮函数的本质是:
用【当前右半边】 + 【当前这一轮的密钥】算出一坨东西”
可以得
L1’和R1’是中间状态
1 | 当前状态: (L2, R2) |
所以是:
1 | F(R2, K1) |
其实就是Lnew = Rold,那(这里的解密其实还是加密)Lnew只是叫做了L1’
1 | new_L = old_R; |
所以有上面那个
我们要理解这个流程需要先明确解密这一轮用的正是当初加密时用过的那一坨密钥相关信息
可以得到R’1=L1
Feistel 的可逆性不是一个 XOR 表达式抵消另一个 XOR 表达式,而是加密的一整轮,被解密的一整轮反向抵消,这个不太好理解,抵消已经发生在了操作序列里了
被 XOR 两次的是第 2 轮里由 K1 注入的那部分信息(已经注入到R2),其源头是 F(R1, K1)。解密时不是直接消掉,而是通过轮结构,把这次注入整体 undo 掉
所以: (L1’, R1’) = (R2, L1) 等价于加密第1轮结束状态
再跑一轮可以得到(L0, R0)
当然上面的原理不懂没关系,我们只需要知道patch密文进输入,对调keys如原本顺序是0-15改成15-1即可
断在此处把密文写入input
1 | import ida_bytes |
稍微看下就可以发现是16组key
1 | import ida_bytes |
KEYS处下断点,调试就行
flag{Fun_Fact_its_littleEndian_DES}
此外的变种有3-DES…
不过这些都是比较偏密码了,逆向一般不会到魔改3-DES这种
AES
AES 高级加密标准算法,其发展是从1997年开始的,AES其主要是用于替换DES而产生的,该算法具有128位的分组长度,支持192/256位的密钥长度,其算法仅支持128/192/256的密钥长度,分别称作AES-128,AES-192,AES-256。
原理直接看我的AES加密分析 | Matriy’s blog
1 | from Crypto.Cipher import AES |
1 |
|
1 |
|
最主要时识别AES盒和AES的S盒逆
SM4
1 | # 魔改S盒 |
1 | from gmssl.sm4 import CryptSM4, SM4_DECRYPT |
识别S盒(可魔改),和FK CK
1 |
|
其它
Base64
换表base64
1 | import base64 |
1 | table = [0x4f,0x4c,0x4d,0x4a,0x4b,0x48,0x49,0x46,0x47,0x44,0x45,0x42,0x43,0x40,0x41,0x5e,0x5f,0x5c,0x5d,0x5a,0x5b,0x58,0x59,0x56,0x57,0x54,0x6f,0x6c,0x6d,0x6a,0x6b,0x68,0x69,0x66,0x67,0x64,0x65,0x62,0x63,0x60,0x61,0x7e,0x7f,0x7c,0x7d,0x7a,0x7b,0x78,0x79,0x76,0x77,0x74,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x25,0x21] |
RSA
1 | import libnum |
1 | import gmpy2 |
PRESENT
遇到的一题:
1 | Sbox = [12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2] |
1 | SBOX = [12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2] |
还有一些如维吉尼亚的….偏misc一般逆向里不会出现,这里不再赘述

























