在数据保护和数据转换算法中,rc4和base64是使用频率最高的算法;rc4用于数据加密,base64用于数据转换。
本篇文章介绍的识别算法和上一篇稍有不同,本篇讲述的算法识别方法我把它称之为“结构和特征识别”的识别方法;该方法适用于算法简单,算法结构逻辑清晰,并且算法中包含或者算法输出包含明显的特征,把这些明显的固定的特征抽取加以整理就构成了识别这些算法的关键。
base64识别
介绍识别方法之前我们先看一下base64原理。
Base64 的核心点:
1. 每 3 个字节 → 转成 4 个字符
一个字节是 8 位(bit),3 个字节是 3 x 8 = 24 位
把这 24 位分成 4 组,每组 6 位(因为 6 x 4 = 24)
每个 6 位的值范围是 0 ~ 63,所以可以查一个“64个字符的表”,得到 4 个可打印字符
2. Base64 使用的 64 个字符是:
A–Z(26个) a–z(26个) 0–9(10个) + 和 /(2个)
所以一共有 64 个字符,叫做 “Base64”。
3.补位(padding)
如果不是刚好 3 个字节,比如你只给了 "Ma"(2 个字符 = 16 位)怎么办?
→ Base64 会补零到 24 位,然后结果里加一个 = 做标志。
规则:
如果最后只有 1 个字节(8位),补 4 个 0,末尾加 ==
如果最后有 2 个字节(16位),补 2 个 0,末尾加 =
其他略
结构特征描述
1. 把三个字节变成四个字符(后面我们着重讲一下)
2. Base64依赖一个64个字符的表
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
3. 输出可能会出现“=”作为补位,如”Hello, world!”的base64为SGVsbG8sIHdvcmxkIQ==
把三个字节变成四个字符
举个例子(超简单)
假设我们要编码文本:
"Man"
它的 ASCII 是:
M: 77 → 二进制 01001101
a: 97 → 二进制 01100001
n: 110 → 二进制 01101110
拼在一起:
01001101 01100001 01101110 → 共24位
把这 24 位切成 4 组,每组 6 位:
010011 010110 000101 101110
从上面base64原理举例中我们抽取核心特征是三个字节变成四个字符,一个字节8位,共24位,在把24位切成4组,每组6位;这不是妥妥的位运算嘛,位运算就涉及左右移位、或与操作。
算法特征抽取
由以上分析得,base64算法具备如下直观的特征:
1、 取三个字符组成24位二进制数,在此基础上进行移位或与操作后得到每组6位的二进制数共4组
2、 有一个固定的字符表是(参与算法索引字符):
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
3、 输出字符串中可能包含”=”
对特征1有很多种实现,我贴图出来参考一下;
实现一:
实现二:
实现三(这种实现不多见, 它不把三个字节对应的二进制数组合成24位,而是独立看待每个字节的二进制数,组合拼接形成每组6位的4组二进制数):
我们需要从前两种(第三种结合原理去分析)不同的实现中抽取相同的特征作为识别算法的特征,从输入字符(这里不是很准确,应该是任意二进制字节,有些二进制数不是可显示字符)序列连续取三个字节,左移8、16位或操作后形成一个24位的二进制数,先移位后与(右移6、12、18位,然后都与0x3f(十进制数63)); 先与后移位(分别与0xfc0(十进制数4032),0x3f00(十进制数258048), 0xfc000(十进制数16515072),在对应移位18、12、6。
最终我们得到快速定位base64算法的特征如下:
1、看输出结果是否包含”=”,算法中是否包含一个固定64个字符的表
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
2、由抽取实现方式简化得到;看左移8、16位,右移6、12、18位,&四个常数0xfc0、0x3f00、0xfc000、0x3f
示例
我先以一段arm v7汇编举例,示例中通过base64固定的字符串替换表(64个)交叉引用找到了算法位置,然后通过base64原理看移位等推断它是base64算法。
定位字符串:
交叉引用:
定位函数:
伪代码(base64后两位可能涉及补码=的情况,这里忽略,不给出演示):
伪代码和我们上面介绍的实现三比较接近,我们来分析一下,v9是计数器从0开始,v4是输入的原始数据,(v4 + v9) >> 2等价于byte1 >> 2,整行是把计算的值直接作为base64表的索引查找出了替换的值即结果;v11是 (v4 + v9)即byte1, 16 * v11即v11(byte1) << 4 & 0x30 等价于(v11 & 3) << 4,作为第二个字符的高2位,v10是原始输入数据序列的下一个字节byte2,右移4位同刚刚计算得出的高2位与操作即第二个替换后的字符的索引即((byte1 << 4) & 0x30) | (byte2 >> 4),其他代码逻辑类似,这里就不在做分析了。
上面识别方法是通过特征base64固定的替换表,和简单的base64核心逻辑特征确定了上面算法是base64;基于算法的实现存在差异(逻辑相同),其他算法可能包含实现1和实现2的移位操作和特殊的常量数字(0x3f, 0x3f00,……),这些都是我们识别base64算法的核心;base64算法如此的简单以至于它太容易被识别,因此出现了vmp化的base64,被vmp化后我们无法直观识别,但当我们动态分析时,它的特征就会一一暴漏,这样我们就能快速识别它了,另外如果某个算法最终输出是一段长长的字符串且末尾中最多包含两个”=”,那我们首先假设它就是base64算法。
rc4识别
有了识别base64的基础识别rc4就更容易了,虽说这两个算法毫无关系,但因为rc4算法简单,因此结构和特征识别方式同样适用于rc4。同样我们看一下rc4的核心逻辑:
rc4核心点:
用密钥生成一个“伪随机序列”,然后和明文按位异或(XOR)得到密文。
你只要记住这句话,RC4的原理你已经掌握了一半。
RC4 工作流程分两步:
KSA(Key-Scheduling Algorithm) 用密钥打乱一个 0~255 的数组(称为 S-box)
PRGA(Pseudo-Random Generation Algorithm) 生成一个伪随机字节流(keystream) 明文和这个流逐字节 XOR,就得到了密文
KSA
初始化 S 盒(S-box)
准备一个数组 S,包含从 0 到 255 的整数(共 256 个数):
S = [0, 1, 2, ..., 255]
再准备一个密钥数组 K,用密钥循环填充:
key = "key" → ASCII:[107, 101, 121]
K = [107, 101, 121, 107, 101, 121, ..., 共 256 个]
KSA(Key-Scheduling Algorithm)
我们开始打乱 S 数组:
j = 0
for i = 0 to 255:
j = (j + S[i] + K[i]) % 256
swap(S[i], S[j])
这个过程的目的就是用密钥来打乱初始数组 S,从而产生一个独一无二的 S-box。
KSA结构性描述
1、 一个可有可无的大小为256的数组S,数组中元素值即为索引值(可能通过循环获得)
2、 一个循环(256次),对大小为256的数组K不停的重复填充输入密钥,如密钥是“ABC”,则数组K为[A,B,C,A,B,C……](可能在循环中通过索引直接获取密钥对应数据)
3、 又一个循环(256次),计算某个索引(几个数的和),数据交换(S数组元素交换)
PRGA
PRGA(生成伪随机序列)
初始化:
i = 0 j = 0
开始生成密钥流(每生成 1 字节加密 1 字节):
循环每个字节:
i = (i + 1) % 256
j = (j + S[i]) % 256
交换 S[i] 和 S[j]
t = (S[i] + S[j]) % 256
keystream_byte = S[t]
这个 keystream_byte 就是你用来和明文 XOR 的密钥流。
加密(XOR)
每个明文字节和密钥流字节异或:
cipher_byte = plaintext_byte ⊕ keystream_byte
对每个字符都这样做,最终得到加密后的字节序列。
PRGA结构性描述
1、 又一次循环(最后一次),循环次数是输入数据的长度(流式加密顺序的逐个的)
2、 又一次数据交换,得到一个异或因子
3、 取输入数据(按顺序一个字节)和异或因子异或得到加密后的值即输出结果
rc4算法特征抽取
由以上对rc4的原理详细分析,我们可有抽取一些直观的特征:
1、 包含多次循环,循环次数均循环256次,256是一个常数(除最后一次循环)
2、 无论是否由循环得到,总有一个大小为256的数组,并且数组元素值即为索引值(初始S-box)
3、 某个循环(256次)计算了很多数据的和,同时用该值作为数组项交换的索引
4、 最后一次循环次数等于输入数据长度,该循环包含数组数据交换,同时存在异或操作
看一下源代码(c实现)
从源码中我们能看到存在三个大循环,除最后一个都是循环256次,存在多个循环256次的循环需要被注意;很多和的计算,很多数组的交换,同时在最后一个循环中存在异或操作。
示例
KSA(两个循环,次数均为256)
伪代码(重命名修改过):
PRGA
伪代码(重命名过):
以上两个部分合在一起就构成了完整的rc4,总结rc4算法的识别最简单的特征就是算法包含多次循环,并且多数循环的循环次数是256,算法中数字256出现的频率特别多,算法之初会构造(或者默认提供)一个256大小的数组,并且数组元素值为其索引;另外算法中包含异或操作,并且异或后的值作为最终结果存储,基本就可有断定它是rc4了;如果你还想十分肯定它确实是那就依据rc4算法其他特征来做最终的判定。
rc4是比较经典的流式加密算法,做产品一般不会对算法进行修改,但也会存在个别人或者企业偷偷对算法进行一点点的修改(每个加密算法都有很深的数学和密码学原理,胡乱修改会导致算法失效或者加密强度、性能降低等),它仍被认为是rc4算法。
rc4算法比较简单,因此很多vmp化rc4很常见,但它的原理结构和执行流程不变,当我们动态分析一个算法时,如果遇到算法出现多次循环(循环次数256),很多数组都是256大小,甚至256作为变量出现很多次,同时在某个循环体内存在异或操作,那基本就可有肯定它是rc4了。
好,本篇文章到此就结束了,它和上篇文章有相似之处,都是在找固定的东西,就像病毒查杀一样,把它们收集起来作为识别的指纹,简单算法一样,其实复杂算法也一样,也可以固定特征用于识别它们,只不过需要做到效率折中,希望这两篇文章能帮助刚刚入门不久或者经验不多的工程师们。