从CTF和勒索样本中学习AES加密算法逆向识别
字数 1602 2025-08-22 12:23:36
AES加密算法逆向识别与实战分析
1. AES加密算法概述
AES(Advanced Encryption Standard)又称Rijndael加密法,是用于取代DES的对称加密算法,具有以下特点:
- 分组大小为128位的分组密码
- 支持三种密钥长度:128位、192位和256位
- 软硬件实现高效
- 加密轮数:128位10轮、192位12轮、256位14轮
- 明文长度必须是16的倍数,不足时需要填充
1.1 填充模式
常见填充模式包括:
- NONE
- PKCS7
- ZERO
- ANSI923
- IOS7816_4
- IOS10126
2. AES加密流程详解
2.1 加密过程概述
完整加密流程分为三个阶段:
-
初始轮(1):
- AddRoundKey:明文矩阵与第一轮密钥异或
-
中间轮(2~9/11/13):
- SubBytes:字节替换
- ShiftRows:行移位
- MixColumns:列混淆
- AddRoundKey:轮密钥加
-
最终轮(10/12/14):
- SubBytes
- ShiftRows
- AddRoundKey
2.2 核心组件
2.2.1 State(状态矩阵)
- 将明文数据分成多个4×4的数据块
- 第一个字节位于第一列第一行,第二个字节位于第一列第二行,以此类推
2.2.2 S盒(S-Box)
- 用于SubBytes操作的替换表
- 加密使用S-Box,解密使用逆S-Box
- 标准AES提供固定的S-Box表
2.2.3 密钥扩展(Key Expansion)
将原始密钥扩展为轮密钥数组:
# Round Constants (AES-128)
RCON = [None, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36]
def subword(self, word):
"""AES SubWord方法"""
bs = struct.pack('>I', word)
nw = []
for b in bs:
nw.append(self.s_box[b])
nw = struct.unpack('>I', bytes(nw))[0]
return nw
def rotword(self, word):
"""AES RotateWord方法"""
bs = list(struct.pack('>I', word))
bs.append(bs.pop(0))
nw = struct.unpack('>I', bytes(bs))[0]
return nw
def key_expansion(self, key):
"""AES密钥扩展算法"""
ek = []
i = 0
# 保存原始密钥
while i < 4: # AES-128为4个字
b = bytes(key[i*4:(i*4)+4])
w = struct.unpack('>I', b)[0]
ek.append(w)
i += 1
# 生成扩展密钥
while i < 44: # AES-128生成44个字
tmp = ek[i-1]
if i % 4 == 0:
rcon_val = struct.unpack('>I', bytes([self.RCON[i//4], 0, 0, 0]))[0]
tmp = self.subword(self.rotword(tmp)) ^ rcon_val
nw = tmp ^ ek[i-4]
ek.append(nw)
i += 1
return ek
2.3 加密操作详解
2.3.1 AddRoundKey(轮密钥加)
将数据矩阵与密钥矩阵进行逐字节异或:
def add_round_key(s, k):
for i in range(4):
for j in range(4):
s[i][j] ^= k[i][j]
2.3.2 SubBytes(字节替换)
使用S-Box进行字节替换:
def sub_bytes(s):
for i in range(4):
for j in range(4):
s[i][j] = s_box[s[i][j]]
2.3.3 ShiftRows(行移位)
- 第一行不变
- 第二行左移1字节
- 第三行左移2字节
- 第四行左移3字节
def shift_rows(grid, inv=False):
for i in range(4):
if inv: # 解密
grid[i::4] = grid[i::4][-i:] + grid[i::4][:-i]
else: # 加密
grid[i::4] = grid[i::4][i:] + grid[i::4][:i]
2.3.4 MixColumns(列混淆)
在GF(2^8)有限域做矩阵乘法:
def mix_columns(grid):
def mul_by_2(n):
s = (n << 1) & 0xff
if n & 128:
s ^= 0x1b
return s
def mul_by_3(n):
return n ^ mul_by_2(n)
def mix_column(c):
return [
mul_by_2(c[0]) ^ mul_by_3(c[1]) ^ c[2] ^ c[3],
c[0] ^ mul_by_2(c[1]) ^ mul_by_3(c[2]) ^ c[3],
c[0] ^ c[1] ^ mul_by_2(c[2]) ^ mul_by_3(c[3]),
mul_by_3(c[0]) ^ c[1] ^ c[2] ^ mul_by_2(c[3])
]
for i in range(0, 16, 4):
grid[i:i+4] = mix_column(grid[i:i+4])
3. AES优化实现:T表(T-Table)方法
为提高效率,将MixColumns、ShiftRows和SubBytes合并为查找表操作。
3.1 T表生成
def gmul(a, b):
"""AES专用乘法函数"""
p = 0
for c in range(8):
if b & 1:
p ^= a
a <<= 1
if a & 0x100:
a ^= 0x11b
b >>= 1
return p
def generate_t_tables(self):
"""生成AES查找表方法使用的T表"""
self.t1 = []
self.t2 = []
self.t3 = []
self.t4 = []
self.t5 = []
for i in range(len(self.s_box)):
word1 = [gmul(self.s_box[i], 2), self.s_box[i], self.s_box[i], gmul(self.s_box[i], 3)]
word2 = [gmul(self.s_box[i], 3), gmul(self.s_box[i], 2), self.s_box[i], self.s_box[i]]
word3 = [self.s_box[i], gmul(self.s_box[i], 3), gmul(self.s_box[i], 2), self.s_box[i]]
word4 = [self.s_box[i], self.s_box[i], gmul(self.s_box[i], 3), gmul(self.s_box[i], 2)]
word5 = [self.s_box[i]] * 4
self.t1.append(struct.unpack('>I', bytes(word1))[0])
self.t2.append(struct.unpack('>I', bytes(word2))[0])
self.t3.append(struct.unpack('>I', bytes(word3))[0])
self.t4.append(struct.unpack('>I', bytes(word4))[0])
self.t5.append(struct.unpack('>I', bytes(word5))[0])
3.2 基于T表的加密实现
def cipher_t_table(self, pt):
"""查找表实现的AES加密"""
ct = []
around_key = [0, 0, 0, 0]
tmp = [0, 0, 0, 0]
# 初始化轮密钥
around_key[0] = struct.unpack('>I', pt[0:4])[0] ^ self.ek[0]
around_key[1] = struct.unpack('>I', pt[4:8])[0] ^ self.ek[1]
around_key[2] = struct.unpack('>I', pt[8:12])[0] ^ self.ek[2]
around_key[3] = struct.unpack('>I', pt[12:16])[0] ^ self.ek[3]
# 前9轮加密
for round in range(1, 10):
for i in range(4):
tmp[i] = (self.t1[(around_key[i] >> 24) & 0xff] ^
self.t2[(around_key[(i+1)%4] >> 16) & 0xff] ^
self.t3[(around_key[(i+2)%4] >> 8) & 0xff] ^
self.t4[(around_key[(i+3)%4]) & 0xff] ^
self.ek[(round*4)+i])
around_key = tmp.copy()
# 最后一轮加密
for i in range(4):
ct.append(self.s_box[(around_key[i] >> 24) & 0xff] ^ ((self.ek[-4+i] >> 24) & 0xff))
ct.append(self.s_box[(around_key[(i+1)%4] >> 16) & 0xff] ^ ((self.ek[-4+i] >> 16) & 0xff))
ct.append(self.s_box[(around_key[(i+2)%4] >> 8) & 0xff] ^ ((self.ek[-4+i] >> 8) & 0xff))
ct.append(self.s_box[(around_key[(i+3)%4]) & 0xff] ^ ((self.ek[-4+i]) & 0xff))
return bytes(ct)
4. AES逆向识别实战
4.1 强网拟态2024 babyre题目分析
-
识别加密函数:
- 主函数调用sub_401550进行解密
- sub_401bce被调用3次,疑似AddRoundKey操作
-
识别S-Box:
- 作者将S-Box写成4字节间隔,增加识别难度
- 可通过标准S-Box交叉验证
-
识别密钥扩展:
- 查找轮常量(RCON)特征
- 识别密钥扩展函数中的S-Box替换和轮常量异或操作
4.2 Sodinokibi勒索软件分析
-
识别T表:
- 计算T表前4字节:0xc66363a5, 0xa5c66363, 0x63a5c663, 0x6363a5c6
- 在IDA中搜索这些特征值定位T表
-
识别加密流程:
- 跟踪T表的使用位置
- 识别轮密钥加操作
- 根据轮数判断AES版本(128/192/256)
5. 逆向识别技巧总结
-
识别S-Box:
- 查找256字节的固定表
- 标准S-Box以0x63开头
-
识别轮常量(RCON):
- 查找0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36序列
-
识别T表:
- 查找4个256项的查找表
- 计算前几项验证是否为T表
-
判断AES版本:
- 根据轮数:10轮(AES-128), 12轮(AES-192), 14轮(AES-256)
- 根据密钥扩展后的轮密钥数量
-
识别加密模式:
- ECB模式:相同的明文块产生相同的密文块
- CBC模式:需要初始向量(IV),状态矩阵作为下一轮输入