众所周知,AES 算法是我们在安卓逆向中的一个比较重要的算法,掌握它的算法细节对于我们逆向无疑是有很大的帮助的
这一块呢,看的是龙哥的文章来学习的,为了记个笔记,就照搬了一下龙哥的图,致敬大佬
AES 分类
| 分类 | 密钥长度 | 轮密钥长度 | 扩展密钥长度 | 分组长度 | 加密轮数 |
|---|---|---|---|---|---|
| AES-128 | 16 字节 | 16 字节 | 16*11=172 | 16 字节 | 10 |
| AES-192 | 24 字节 | 16 字节 | 16*13=208 | 16 字节 | 12 |
| AES-256 | 32 字节 | 16 字节 | 16*15=240 | 16 字节 | 14 |
上面的这三个算法,除了密钥编排算法不同和加密的轮数不一样之外,其余的计算逻辑都是相同的
CBC 模式 IV 都是 16 个字节,CBC 计算逻辑相同
- AES-128 密钥编排算法中 K0 是原始密钥,共 16 个字节
- AES-192 密钥编排算法中 K0 和 K1 的前半部分是原始密钥,共 24 个字节
- AES-256 密钥编排算法中 K0 和 K1 是原始密钥,共 32 字节
填充模式
在 AES 中,识别常见的明文填充模式也是比较重要的,下面是几种常见的填充模式:
PKCS #7 Padding
这个是目前最标准,最通用的填充方式
- 规则:假设需要填充 N 个字节,则填充的 N 个字节的值都是 N(16 进制)
- 特殊情况:如果明文长度正好是 16 字节的整数倍,则额外填充一个完整的块(即 16 个字节,每个字节都是 0x10,即十进制的 16),以便解密时能正确的移除填充
- 示例:
- 缺 3 个字节:
03 03 03 - 缺 1 个字节:
01 - 正好整除:
10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
- 缺 3 个字节:
PKCS #5 Padding
PKCS #5 标准严格规定分组长度 8 字节(64 位)
- 计算缺失长度:计算明文距离下一个 8 字节倍数还差多少字节,记为 N
- 填充内容:在明文末尾添加 N 个字节,每个字节的值都等于 N(16 进制表示)
- 特殊情况:如果明文长度正好是 8 字节的整数倍,则额外填充一个完整的块(即填充 8 个字节,每个字节的值都是 0x08)
- 示例:
- 明文长度不足 8 个字节
- 明文:
A B C D E(5 字节) - 填充:
A B C D E 03 03 03
- 明文:
- 明文长度超过 8 个字节但未满 16 字节
- 明文:
A B C D E F G H I J(10 字节) - 填充:
A B C D E F G H I J 06 06 06 06 06 06
- 明文:
- 明文长度正好是 8 的倍数
- 明文:
A B C D E F G H(8 字节) - 填充:
A B C D E F G H 08 08 08 08 08 08 08 08
- 明文:
- 明文长度不足 8 个字节
Zero Padding(零填充)
直接填充 0,缺 3 个字节则填充 00 00 00
No Padding
- 规则:不进行任何填充
- 使用条件:
- 明文长度严格等于 16 的倍数
- 或者使用了不需要填充的工作模式
工作模式
下面我总结成了一个表
| 模式 | 是否需要填充 | 是否需要 iv |
|---|---|---|
| ECB | 是 | 否 |
| CBC | 是 | 是 |
| CTR | 否 | 是 |
| GCM | 否 | 是 |
| CFB | 否 | 是 |
| OFB | 否 | 是 |
计算流程
下面主要以 AES-128 为例,其他的也是一样的
AES-128 接收 16 字节的明文输入,16 字节的密钥,输出 16 字节的密文结果
设置 key 为 2b7e151628aed2a6abf7158809cf4f3c,明文为 00112233445566778899aabbccddeeff
AES 的整体可以分成左右两块,即明文的处理和密钥的编排
明文的处理主体是一个初始化轮密钥加和十轮运算,在初始化轮密钥加十轮运算中都需要使用密钥编排的结果
密钥编排将 16 个字节经过运算推演出 11 组轮密钥,每一组 16 个字节,称之为 K0,K1…K10
密钥编排
首先来看一下密钥是如何算出来扩展密钥的,上面设置的 key 是 2b7e151628aed2a6abf7158809cf4f3c,为了区分密钥和密钥编排后的轮密钥,我们将此时的密钥叫做主密钥
在 AES-128 中,密钥扩展后得到 $17 \times 16=176$ 字节, 使用时逐 16 字节划分成 $K_0,K_1,……,K_{10}$,但是在生成时,它是逐 4 字节生成的,即 44*4。不妨用数组来描述它,即一个包含了 44 个元素的数组,叫 $W$
这 44 个元素生成的规则有三种,如下图所示:

不同颜色代表了不同规则,最上方蓝色区域的就是主密钥本身切成四段
左侧的红色部分,$W_4,W_8,W_{12},……,W_{40}$ 的生成稍复杂一点
xor 是异或运算,比如: $W_4 = g(W_3)\quad xor\quad W_0$ 。g(当前元素前面那个元素)异或当前元素头顶上那个元素
那么关键点就是这个 g 函数了,g 函数一个分三个步骤–循环左移,S 盒替换,字节异或,下面以 $W_4$ 运算中所需的 $W_3$ 为例。
首先是循环左移,规则固定–将最左边的一个字节挪到右边即可

第二步就是 S 盒替换,将数值本身作为索引取出 S 数组中对应的值。S 盒是固定的,在逆向的过程中经常会用来判断 AES 的标志
|
|
虽然 S 盒背后有十分复杂的知识,但是我们逆向算法,一般用不着去了解

最后一个步骤简单,将上一步得到的结果中的最高字节与一个固定常量异或。$W_4$ 的生成是第一个,用如下 rcon 表的第一个元素 0x01。$W_{40}$ 即第 10 次,用最后一个元素 0x36
|
|
最终结果

下面红色的部分同理
接下来就应该看一下橙色的部分了,和红色的部分类似,去掉 g 函数即可

例如: $W_5 = W_4\quad xor\quad W_1=0xa0fafe17\quad xor\quad 0x28aed2a6 = 0x88542cb1$
如下就是密钥编排的完整的算法:
|
|
运行结果如下:
|
|
明文处理
密钥编排部分说完了,接下来该看一下明文的处理了即上面流程图中左边的部分
首先,需要调整明文的格式,在 AES 中,数据以 $state$ 的形式计算,中间存储与传输,中文名即状态
从明文转到 state 形式很简单,以我们上面的那个明文为例: 00112233445566778899aabbccddeeff,从上到下,从左至右

接着,轮密钥加,为什么叫轮密钥加呢?因为在算法中加就是异或
这个是第一次轮密钥加的步骤,所以也叫做初始轮密钥加,也就是将明文的 state 状态与初始轮密钥进行逐字节异或

接下来就是 10 轮主运算,看如下的伪代码,可以清楚的看到一轮运算中有什么,以及第十轮和前九轮有什么区别

初始的明文转 state 和最后的 state 转明文自不用说,然后是初始轮密钥,使用 $K_0$
前 9 轮运算中,包含四个步骤:字节替换,循环左移,列混淆,轮密钥加
第 10 轮种,包含三个步骤:字节替换,循环左移,轮密钥加,相比较于前 9 轮,少了一个列混淆,其余的都是相同的
而字节替换步骤,和密钥编排中的 S 盒替换完全一致
循环左移,和密钥编排中的循环左移类似,但不完全相同。在密钥编排中,函数中也需要循环左移,但其中待处理的数据仅有一行,而明文编排中有 4 行,其循环左移规则如下:
第一行不循环左移,第二行循环左移 1 字节,第三行循环左移 2 字节,第四行循环左移 3 字节

列混淆有点麻烦啊,就不写了吧
所有的代码如下:
|
|
AES CBC 模式的计算流程
当 AES 加密是 CBC 模式的时候,明文首先会与 IV 异或,然后将结果进行块加密,输出明文,同时本次输出密文作为下一个块加密的 IV

IV 同样也需要转为 state 然后与明文的 state 进行异或
例如 IV 是 00112233445566778899aabbccddeeff

仅需要修改以下代码就可以实现 AES 的 CBC 加密
|
|
参考文章
龙哥星球文章
https://www.yuque.com/nanren-w8l2z/xgu63m/uoff9ovsmhqki9wh?singleDoc#cjBWY