0x01 简介
AES加密,为对称加密算法,分组输入分组输出。3种AES对应的密钥长度、分组长度和轮数如下表。
密钥长度(N个32字节双字) | 分组长度(N个32位双字) | 轮数 | |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
0x02 算法原理
算法加密流程如下图。主要包含4种轮函数:字节代换(SubByte)、行移位(ShiftRow)、列混合(MixColumn)、轮密钥加(AddRoundKey)。
AES-128加密主要分为下面5步:
- 密钥扩展得到每一轮的密钥,将输入复制到状态数组中
- 先进行初始密钥加
- 再进行9轮的字节代换、行移位、列混合、轮密钥加
- 最后一轮进行字节代换、列混合、轮密钥加(没有行移位)
- 将最后的结果复制到输出数组中
字节代换(subBytes)
(不考虑数学原理)字节代换可以当成一个简单的查表操作。AES定义了一个16*16字节的S-box。以状态数组中的每个字节元素的高4位作为行标,低4位作为列标,取出相应的元素作为SubBytes操作。例如,十六进制值(C5),高4位为C,低4位为5,取出S-box中的C行5列的A6替换C5。
行移位(ShiftRow)
对状态数组进行行移位操作。第一行保存不变,第2行循环左移1字节,第3行循环左移2字节,第4行循环左移3位。
列混合(MixColumn)
列混合可以看成是一个矩阵乘法的过程
轮密钥加(AddRoundKey)
将状态中的元素与轮密钥通过异或得到新的状态元素
密钥扩展(Key Expansion)
密钥扩展算法是Rijndael的密钥编排实现算法,其目的是根据种子密钥(用户密钥)生成多组轮密钥。轮密钥为多组128位密钥,对应不同密钥长度,分别是11,13,15组。11个子密钥存储在W[0]到W[43]中,第0轮为将128位的密钥填入W[0],W[1],W[2],W[3]中。其他的W计算方法如下:
- W[4i]=W[4(i-1)]+g(W[[4i-1])
- W[4i+j]=W[4i+j-1]+W[4(i-1)+j]
其中g的计算方法如图的左边部分。
- 将4个循环左移
- 执行按字节的S盒代换
- 轮系数Rcon[i]与最左边的字节相加。Rcon[11] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 }
0x03 逆向分析
这里用的例子是加密与解密中的AESKeyGenMe.exe
利用PEiD的插件KANAL识别,可以识别到MD5和AES的S盒、逆S盒
用ida pro中的Findcrypt同样是识别到了
找到check函数。先对输入的用户名和序列号长度进行判断,如果用户名长度等于0,或者序列号长度!=32都显示Wrong Serial!
sub_4012F0函数
根据之前的学习,可以基本判断这是MD5Init函数,初始化了context
sub_401320函数
上面已经识别到MD5Init函数,可以猜测后面的函数为MD5Update和MD5Final。其中sub_401440函数为MD5Transform。所以基本可以确定先对输入的用户名做一个MD5的计算。
sub_401EC0函数(aes_init)
函数原型为aes_init(aes* a,int mode,int nk,char* key,char* iv),主要是对aes结构体进行初始化。
- a是AES结构体
- mode是AES的工作模式,这里采用的是ECB
- nk是密钥长度,0x10
- key指向密钥数组,这里可以从v7数组得到{0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c}
- iv是初始化向量,用于CBC模式,ECB用不到,所以设为NULL
sub_4023A0函数(aes_encrypt)
aes_init只后调用了aes_encrypt,根据aes结构体中的模式会去调用aes_ecb_encrypt。如下图所示。
所以这个KeyGenMe.exe的逻辑
- 比较输入的用户名和序列号长度是否正常
- 将输入的用户名进行MD5得到MD5_username
- 将序列号进行aes加密得到AES_serial
- 将得到的MD5_username和AES_serial进行比较
keyGen的制作就是将MD5(username)得到的值,用AES解密,即可得到序列号。
keyGenMe
可以利用openssl快速实现keyGenMe.用下面的编译命令来编译keyGen
gcc keyGenMe.c -o keyGenMe -lssl -lcrypto
#include<stdio.h>
#include<openssl/aes.h>
#include<openssl/md5.h>
int main(){
MD5_CTX md5_ctx;
unsigned char username[] = "pediy";
unsigned char md5[16];
unsigned char serial[16];
MD5_Init(&md5_ctx);
MD5_Update(&md5_ctx, username, strlen((char*)username));
MD5_Final(md5,&md5_ctx);
printf("字符串%s的MD5码为:",username);
for (int i = 0; i < 16; i++) printf("%02x", md5[i]);
printf("\n");
//AES解密
unsigned char key[] = {0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c};
AES_KEY aes;
AES_set_decrypt_key(key,128,&aes);
AES_decrypt(md5,serial,&aes);
printf("AES解密:");
for (int i = 0; i < 16; i++) printf("%02x", serial[i]);
printf("\n");
return 0;
}
0x04 总结
现有工具的AES识别主要是集中在S盒和逆S盒。其实RCON数组,或者是其他一些结构体也可以用来对魔改AES进行识别。
《加密与解密第4版》
《深入浅出密码学》
https://github.com/matt-wu/AES