详解白盒AES以及C代码实现(以CTF赛题讲解白盒AES)
字数 983 2025-08-22 12:22:15

白盒AES详解及C代码实现

1. AES加密流程概述

标准AES加密流程伪代码如下:

state ← plaintext
AddRoundKey(state, k0)
for r = 1 ... 9
    SubBytes(state)
    ShiftRows(state)
    MixColumns(state)
    AddRoundKey(state, kr)
SubBytes(state)
ShiftRows(state)
AddRoundKey(state, k10)
ciphertext ← state

2. 白盒AES实现原理

2.1 流程调整

为了实现基于查表的白盒AES,需要对标准流程进行调整:

  1. 将初始AddRoundKey(state,k0)放入循环
  2. 将第9轮的AddRoundKey(state,k9)移出循环
  3. 利用ShiftRows是线性变换、SubBytes是映射变换的特性,可以调换它们的位置而不影响结果
  4. 在轮密钥也进行行移位的情况下,AddRoundKey和ShiftRow可以调换位置且结果不变

调整后的伪代码:

state ← plaintext
for r = 1 ... 9
    ShiftRows(state)
    AddRoundKey(state, k_{r-1})
    SubBytes(state)
    MixColumns(state)
ShiftRows(state)
AddRoundKey(state, k_9)
SubBytes(state)
AddRoundKey(state, k_{10})
ciphertext ← state

2.2 查表实现

T-Boxes表

将AddRoundKey和SubBytes两个过程合并为一个查表过程:

void getTbox(u32 expandedKey[44], u8 tbox[10][16][256]) {
    for(int i = 0; i < 10; i++) {
        for(int x = 0; x < 256; x++) {
            u8 state[16] = {x};
            memset(state, x, 16);
            add_round_shiftkey(state, expandedKey + 4*i);
            subByte(state);
            if(i == 9)
                add_round_key(state, expandedKey + 40);
            for(int z = 0; z < 16; z++)
                tbox[i][z][x] = state[z];
        }
    }
}

Tyi Tables表

MixColumns操作可以分解为查表实现:

void getTyiTable(u8 table[4][256][4]) {
    for(int i = 0; i < 256; i++) {
        table[0][i][0] = gmul(i, 0x02);
        table[0][i][1] = gmul(i, 0x03);
        table[0][i][2] = i;
        table[0][i][3] = i;
        
        table[1][i][0] = i;
        table[1][i][1] = gmul(i, 0x02);
        table[1][i][2] = gmul(i, 0x03);
        table[1][i][3] = i;
        
        table[2][i][0] = i;
        table[2][i][1] = i;
        table[2][i][2] = gmul(i, 0x02);
        table[2][i][3] = gmul(i, 0x03);
        
        table[3][i][0] = gmul(i, 0x03);
        table[3][i][1] = i;
        table[3][i][2] = i;
        table[3][i][3] = gmul(i, 0x02);
    }
}

Xor Table表

void getXorTable(u8 table[16][16]) {
    for(int i = 0; i < 16; i++)
        for(int j = 0; j < 16; j++)
            table[i][j] = i ^ j;
}

合并Tboxs和Tyi_tables

void getTyiBox(u8 tbox[10][16][256], u32 tyibox[9][16][256]) {
    u8 tyitable[4][256][4] = {0};
    getTyiTable(tyitable);
    
    for(int r = 0; r < 9; r++)
        for(int x = 0; x < 256; x++)
            for(int j = 0; j < 4; j++)
                for(int i = 0; i < 4; i++) {
                    u32 v0 = tyitable[0][tbox[r][j*4+i][x]][i];
                    u32 v1 = tyitable[1][tbox[r][j*4+i][x]][i];
                    u32 v2 = tyitable[2][tbox[r][j*4+i][x]][i];
                    u32 v3 = tyitable[3][tbox[r][j*4+i][x]][i];
                    tyibox[r][j*4+i][x] = (v0 << 24) | (v1 << 16) | (v2 << 8) | v3;
                }
}

2.3 完整加密实现

void aes_encrypt_by_table(u8 input[16], u8 key[16]) {
    u32 a, b, c, d, aa, bb, cc, dd;
    u8 result[16] = {0};
    u8 tbox[10][16][256] = {0}, xortable[16][16] = {0};
    u32 expandedKey[44] = {0}, tyibox[9][16][256] = {0};
    
    expandKey(key, expandedKey);
    getTbox(expandedKey, tbox);
    getTyiBox(tbox, tyibox);
    getXorTable(xortable);
    
    for(int i = 0; i < 9; i++) {
        shiftRows(input);
        for(int j = 0; j < 4; j++) {
            a = tyibox[i][4*j+0][input[4*j+0]];
            b = tyibox[i][4*j+1][input[4*j+1]];
            c = tyibox[i][4*j+2][input[4*j+2]];
            d = tyibox[i][4*j+3][input[4*j+3]];
            
            aa = xortable[(a >> 28) & 0xf][(b >> 28) & 0xf];
            bb = xortable[(c >> 28) & 0xf][(d >> 28) & 0xf];
            cc = xortable[(a >> 24) & 0xf][(b >> 24) & 0xf];
            dd = xortable[(c >> 24) & 0xf][(d >> 24) & 0xf];
            input[4*j+0] = ((aa ^ bb) << 4) | (cc ^ dd);
            
            aa = xortable[(a >> 20) & 0xf][(b >> 20) & 0xf];
            bb = xortable[(c >> 20) & 0xf][(d >> 20) & 0xf];
            cc = xortable[(a >> 16) & 0xf][(b >> 16) & 0xf];
            dd = xortable[(c >> 16) & 0xf][(d >> 16) & 0xf];
            input[4*j+1] = ((aa ^ bb) << 4) | (cc ^ dd);
            
            aa = xortable[(a >> 12) & 0xf][(b >> 12) & 0xf];
            bb = xortable[(c >> 12) & 0xf][(d >> 12) & 0xf];
            cc = xortable[(a >> 8) & 0xf][(b >> 8) & 0xf];
            dd = xortable[(c >> 8) & 0xf][(d >> 8) & 0xf];
            input[4*j+2] = ((aa ^ bb) << 4) | (cc ^ dd);
            
            aa = xortable[(a >> 4) & 0xf][(b >> 4) & 0xf];
            bb = xortable[(c >> 4) & 0xf][(d >> 4) & 0xf];
            cc = xortable[a & 0xf][b & 0xf];
            dd = xortable[c & 0xf][d & 0xf];
            input[4*j+3] = ((aa ^ bb) << 4) | (cc ^ dd);
        }
    }
    
    shiftRows(input);
    for(int j = 0; j < 16; j++) {
        input[j] = tbox[9][j][input[j]];
    }
}

3. 白盒AES攻击实例

3.1 强网杯dot题目分析

攻击方法:

  1. 通过更改第十轮输入的一个字节(每次不同位置,16个位置)
  2. 得到16个密文结果(与正确结果会有四个字节的不同)
  3. 使用phoenixAES工具推导出第十轮的密钥

关键代码:

import phoenixAES
phoenixAES.crack_file('./tracefile', verbose=0)
# Last round key #N found:
# EA9F6BE2DF5C358495648BEAB9FCFF81

使用aes_keyschedule工具获取完整密钥:

> .\aes_keyschedule.exe EA9F6BE2DF5C358495648BEAB9FCFF81 10
K00: 51574232303233486170707947616D65
K01: BF6B0F928F593CDAEE294CA3A94821C6
K02: EF96BB4160CF879B8EE6CB3827AEEAFE
K03: 0F11008D6FDE8716E1384C2EC696A6D0
K04: 97357039F8EBF72F19D3BB01DF451DD1
K05: E9914EA7117AB98808A90289D7EC1F58
K06: 075124A9162B9D211E829FA8C96E80F0
K07: D89CA74CEB73555D035AAFD195B2A0D
K08: 61797FA0AFCE4AF57FFBE00866A0CA05
K09: 9A0D149335C35E664A38BE6E2C98746B
K10: EA9F6BE2DF5C358495648BEAB9FCFF81

最终获得密钥:QWB2023HappyGame

3.2 m1_read题目分析

题目特点:白盒AES + 异或0x66

攻击步骤:

  1. 使用frida调用函数获取数据
  2. 由于数据被异或过,使用phoenixAES之前要先异或回来
  3. 使用phoenixAES获取最后一轮密钥
  4. 使用aes_keyschedule获取初始密钥
  5. 解密数据

关键代码:

# 异或处理
for i in range(len(data)):
    data[i] = bytes.fromhex(data[i])
raw_data = [''] * 32
for i in range(32):
    for j in range(16):
        raw_data[i] += "%02x" % (0x66 ^ data[i][j])

# 使用phoenixAES
phoenixAES.crack_file('tracefile', verbose=0)
# Last round key #N found:
# B4EF5BCB3E92E21123E951CF6F8F188E

# 解密
key = bytes.fromhex("00000000000000000000000000000000")
cipher = AES.new(key, AES.MODE_ECB)
print(cipher.decrypt(c))

4. 关键函数实现

4.1 密钥扩展

void expandKey(u8 key[16], u32 expandedKey[44]) {
    static u32 rcon[] = {
        0x01000000, 0x02000000, 0x04000000, 0x08000000,
        0x10000000, 0x20000000, 0x40000000, 0x80000000,
        0x1B000000, 0x36000000
    };
    
    for(int i = 0; i < 4; i++) {
        expandedKey[i] = (key[4*i] << 24) | (key[4*i+1] << 16) | 
                         (key[4*i+2] << 8) | key[4*i+3];
    }
    
    for(int i = 4; i < 44; i++) {
        u32 temp = expandedKey[i-1];
        if(i % 4 == 0) {
            temp = (temp << 8) | (temp >> 24);
            temp = SBOX[(temp >> 28) & 0xF][(temp >> 24) & 0xF] << 24 |
                   SBOX[(temp >> 20) & 0xF][(temp >> 16) & 0xF] << 16 |
                   SBOX[(temp >> 12) & 0xF][(temp >> 8) & 0xF] << 8 |
                   SBOX[(temp >> 4) & 0xF][temp & 0xF];
            temp ^= rcon[i/4 - 1];
        }
        expandedKey[i] = expandedKey[i-4] ^ temp;
    }
}

4.2 有限域乘法

u8 gmul(u8 ap, u8 bp) {
    u8 p = 0, a = ap, b = bp;
    while(a != 0 && b != 0) {
        if(b & 1 != 0)
            p ^= a;
        if((a & 0x80) != 0)
            a = (a << 1) ^ 0x1b;
        else
            a <<= 1;
        b >>= 1;
    }
    return p & 0xFF;
}

4.3 字节替换

u8 subByte(u8 byte[16]) {
    for(int i = 0; i < 4; i++)
        for(int j = 0; j < 4; j++)
            byte[i*4+j] = SBOX[byte[i*4+j] >> 4][byte[i*4+j] & 0xF];
}

4.4 行移位

void shiftRows(u8 state[16]) {
    u8 tmp = state[1];
    state[1] = state[5]; state[5] = state[9]; state[9] = state[13]; state[13] = tmp;
    
    tmp = state[2]; state[2] = state[10]; state[10] = tmp;
    tmp = state[6]; state[6] = state[14]; state[14] = tmp;
    
    tmp = state[15];
    state[15] = state[11]; state[11] = state[7]; state[7] = state[3]; state[3] = tmp;
}

5. 总结

白盒AES的核心思想是将AES的各个操作步骤通过预先生成的查表来实现,从而隐藏密钥信息。关键点包括:

  1. 调整AES标准流程以适应查表实现
  2. 将AddRoundKey和SubBytes合并为T-Boxes
  3. 将MixColumns分解为Tyi Tables
  4. 使用Xor Table优化异或操作
  5. 攻击方法通常基于差分分析,通过修改输入观察输出变化来恢复密钥

实际应用中,白盒AES的实现需要考虑性能和安全性的平衡,同时要防范各种侧信道攻击和差分分析攻击。

白盒AES详解及C代码实现 1. AES加密流程概述 标准AES加密流程伪代码如下: 2. 白盒AES实现原理 2.1 流程调整 为了实现基于查表的白盒AES,需要对标准流程进行调整: 将初始AddRoundKey(state,k0)放入循环 将第9轮的AddRoundKey(state,k9)移出循环 利用ShiftRows是线性变换、SubBytes是映射变换的特性,可以调换它们的位置而不影响结果 在轮密钥也进行行移位的情况下,AddRoundKey和ShiftRow可以调换位置且结果不变 调整后的伪代码: 2.2 查表实现 T-Boxes表 将AddRoundKey和SubBytes两个过程合并为一个查表过程: Tyi Tables表 MixColumns操作可以分解为查表实现: Xor Table表 合并Tboxs和Tyi_ tables 2.3 完整加密实现 3. 白盒AES攻击实例 3.1 强网杯dot题目分析 攻击方法: 通过更改第十轮输入的一个字节(每次不同位置,16个位置) 得到16个密文结果(与正确结果会有四个字节的不同) 使用phoenixAES工具推导出第十轮的密钥 关键代码: 使用aes_ keyschedule工具获取完整密钥: 最终获得密钥: QWB2023HappyGame 3.2 m1_ read题目分析 题目特点:白盒AES + 异或0x66 攻击步骤: 使用frida调用函数获取数据 由于数据被异或过,使用phoenixAES之前要先异或回来 使用phoenixAES获取最后一轮密钥 使用aes_ keyschedule获取初始密钥 解密数据 关键代码: 4. 关键函数实现 4.1 密钥扩展 4.2 有限域乘法 4.3 字节替换 4.4 行移位 5. 总结 白盒AES的核心思想是将AES的各个操作步骤通过预先生成的查表来实现,从而隐藏密钥信息。关键点包括: 调整AES标准流程以适应查表实现 将AddRoundKey和SubBytes合并为T-Boxes 将MixColumns分解为Tyi Tables 使用Xor Table优化异或操作 攻击方法通常基于差分分析,通过修改输入观察输出变化来恢复密钥 实际应用中,白盒AES的实现需要考虑性能和安全性的平衡,同时要防范各种侧信道攻击和差分分析攻击。