一、前置知识

AES加密算法解析:

目前大部分情况下,AES加密使用的密钥长度为128bit,即16字节,分组长度为16字节,加密的轮函数会执行10次。对于16字节的而言密钥,首先会执行一个密钥扩展的函数,先将密钥以字节的形式排列成4×4的矩阵,四列中每一列会被合并为一个原始密钥,即W[0]—W[3],再在此基础上生成W[4]—W[43],如下图所示:

密钥扩展的规则:

如果i不是4的倍数,则W[i]=W[i-4]⨁W[i-1] 
如果i是4的倍数,则W[i]=W[i-4]⨁T(W[i-1]) 
其中函数T由3部分组成:
a.字循环:将1个字中的4个字节循环左移1个字节。即将输入字[b0, b1, b2, b3]   变换成[b1,b2,b3,b0]
b.字节代换:对字循环的结果使用S盒进行字节代换
c.轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。 
  轮常量Rcon[j]是一个字,其值见下表

第一轮的密钥是W[4]—W[7],以此类推

在轮函数之前,先由明文矩阵转换为状态矩阵,4×4的每个方格中,将明文转为其十六进制表示的形式,如a→0x61

接着是字节代换,大致原理是把状态矩阵中每个字节的高4位作为行值,低4位作为列值,取出S盒中对应的元素作为输出。例如,加密时,状态矩阵中为0x12,则查S盒的第0x01行和0x02列,得到值0xc9,然后以此替换状态矩阵中原有的0x12

然后是行移位,变换情况如图:

列混合是这里面跟信息安全数学关联比较大的一个操作,涉及到基于GF(2^8)上的二元运算,基本操作是矩阵相乘,得到混合矩阵:

对于一个8位的二进制数来说,使用域上的乘法乘以(00000010)等价于左移1位(低位补0)后,再根据情况同(00011011)进行异或运算,设S1 = (a7 a6 a5 a4 a3 a2 a1 a0),刚0x02 * S1如下图所示:

数学原理不再赘述

最后一个步骤是轮密钥加,将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作,如下图所示。其中,密钥Ki中每个字W[4i],W[4i+1],W[4i+2],W[4i+3]为32位比特字,包含4个字节,轮密钥加过程可以看成是字逐位异或的结果,也可以看成字节级别或者位级别的操作。也就是说,可以看成S0,S1,S2,S3 组成的32位字与W[4i]的异或运算。

二、初步分析

先将源代码编译运行,看一看程序大致是怎么样的

这个加密程序就一个输入,猜测是将输入的值加密后,与已定义的一个字符串相比较,相等即为通过

我们接着拖进IDA分析

main函数之前是先定义了很多访问字节单元和字的变量,而AES正是基于字节的加密方式,继续往下看

这里可以看到,一开始是对两个数据块进行了赋值(中间的两个0是标志),均是字的单位,基址可以看成0A4H,则偏移地址分别是从9H-19H,共16字节,21H-41H,共32字节。

观察一下发现,前者存储的值是31323334...36H,转为十进制是1234567890123456

后者同理,是abcdefghijklmnopqrstuvwxyzabcdef,这两个应该是main函数里面定义的局部变量,有可能是密钥之类的,接着分析

从42H-61H共32个字节,均是字节的单位,但是这里0xFCH这种值转换成十进制后是乱码,于是先持保留意见

这里操作看起来有点复杂,总结起来就是从81H到后面8个dword之间,即32个字节,都被赋值为0

紧接着来到main函数主体,可以看到调用了printf(),scanf()和aesEncrypt()三个子函数,并且易分析得到,scanf()传进来的也就是我们输入的flag存储在var_41这里,而从41H-21H是一个32字节局部变量,应该是被输入覆盖掉了。再看aesEncrypt(),不难看出这个函数共有五个参数,20H即32,var_81即32位为0的变量,var_41即输入的字符串,10H即16,var_19即1234567890123456。

有两组变量满足 长度-值 的对应关系,最后另有一个新变量var_8,赋值为0。

1FH即十进制的31,var_8在中间的loc_40205C代码块执行完后会自增1,然后再与31比较,小于或等于则继续,可以判断这里是一个for循环,如for(i=0;i<=31;i++)。

看一下中间的那个代码块,前后的两个结构的意思是,值为var_8可以看成偏移地址,接着将var_61作为基址,加上var_8的偏移赋值给edx,后面是var_81偏移后赋值给eax,最后两者比较。类似于将两个数组的每一个元素两两比较,再结合比较成功的输出和比较错误的输出函数调用情况来看,如果一次比较没成功则退出,全部比较成功了才算对。

var_81最开始是0,然而最后却将其与var_61进行比较,且均是32位,所以var_81应该是var_41经过AES加密之后得到的32位密文,var_61是内置的那个正确答案,那么var_19就是16位的密钥,尝试解密:

在程序里面输入这个明文

正确

三、进阶分析

刚刚我们解题时跳过了aesEncrypt()这个函数,现从密码学的角度对其进行逆向分析

在进行加密之前,可以看到子函数是先将传进来这五个参数进行了合法值的校验,根据下面print的信息及各自的值,可以把这五个参数对应重命名一下,便于分辨

这里定义了一些局部变量,并且大多赋值为0,不好分析,先往下看

loc_401D56是经判断五个参数均合法后加密的第一层,可以看到调用了memcpy()和keyExpansion()两个函数,后者正是密钥扩展。memcpy()有三个参数,keyLen和key是我们重命名后的已知变量,Dst是未知的局部变量,根据这个函数的定义,意思是截取密钥key的前keyLen位,即16位复制给Dst,那么Dst就是这个子函数里的原始密钥了

再看keyExpansion(),参数分别为Dst,16,var_17C,我们知道密钥扩展的结果是基于原始密钥W[0]-W[3]生成W[4]-W[43],于是这个var_17C就是最终的完整密钥。(我返回上面查看var_17C的定义是发现它只有4位的字节单位,比较疑惑,后来看了源码后发现是取的地址)

1. keyExpansion()

进入keyExpansion()函数,还是先根据报错信息和合法性比较重命名形参

合法性比较通过之后,来到的第一个代码块是loc_4015F7

这里对var_14和var_18两个局部变量(4位dword)进行了赋值,前者取的是aeskey起始地址,后者偏移了0B0H,即44个字,而AES使用的密钥就是44个字,于是var_14就存储着最终扩展的结果

接下来是一个for循环,在main函数里面有同理的分析,这里的循环次数是4,循环的操作是往左,跳出时往右

来看看左边的loc_401611

w是重命名之后的var_14,首先向edx中存入循环变量var_C的数据段基址,w与这个基址add后便是w[var_C]。接着var_C进行了逻辑左移两位的操作,相当于十进制中的乘4,用key与之相加,得到key+4*var_C,等价于取出key的第一个字节,然后ecx左移18H即24位,最终存入edx,这样循环四次(内部)后就可以把全部32位存储完。

最后更新w的数据段基址,循环变量var_C自加1,外部循环四次后就可以初始化原始密钥于w中。

这之后显而易见,进入到for(i=0;i<=9;i++)这个循环中来,可以判定为10轮的密钥迭代扩展,分析左边的loc_401698

结合AES密钥扩展的三个步骤可以判断,前半部分是T函数,前几步是字循环,w基址往后偏移10H=16位是dword里最后一个字的末位,存储在了edx里面,然后又取到了w偏移0CH的地址,这是最后一个字的初始位,接着再进行S盒代换。可以看到,这里先是取了eax的前半部分al中的一个字拷贝到eax里面,再将这个字进行S盒代换,存储到对应位置上,然后接着每次逻辑右移8的倍数,取到每个字节的两个字作为行列,,最终S盒代换结果存到ecx中(这里确实太凌乱了,我是找的规律)。进行S盒代换完毕之后,就同轮常量Rcon进行异或。

这之后,取到w基址+4,即为w[1]的地址存入ecx,再取偏移地址10H=w[4]的地址,存入edx,最后取w的地址+14H=w[5]的地址,edx和ecx做异或运算的值=w[5],存入edx。以此类推,就得到了W[4]-W[7]的值,最后使w的地址整体+10h,循环9轮之后就能得到密钥扩展的结果。

这之后是一个两层循环,外层循环次数为11,内层次数为4,loc_4017A3中,先取到v[var_C]的地址存入edx,再取到w[var_C]的值,复制到edx的位置上,而右下角的外层循环步进规则是:w每次加一个dword单位,v每次减一个,综上可以判断v是解密密钥,

keyExpansion()分析完毕。

返回至aesEncrypt()函数,var_C清零,这里可以看到又是一个两层循环,其中外层循环次数为我们传入加密函数的第五个参数即32,内层次数为10

外层循环里面,第一个代码块是loc_401D9E,调用了loadStateArray()和addRoundKey()两个函数。前者是加载状态矩阵,有data(输入的字符串)和var_1AC两个参数,于是这个函数的作用就是将明文data转换为可供AES进行字节处理的状态矩阵,存入var_1AC中,我们再回去看看它的在栈中的情况。

可以看到,这里是连续四个4位dword的数据段,对应了4×4的状态矩阵。

2. loadStateArray()

进入loadStateArray()中,很明显是一个两层循环,我们先把变量重命名一下,外层循环变量为i,内层为j,循环次数和步进均是1。

主要的部分是loc_401518,如下图:

这里是先取了j的值存入eax,将其×4后存入edx中,然后取得state的初始地址,偏移edx中的值即4j之后存入ecx,于是ecx中的值就是state+4j,对于data,先是把它自身的地址存到了eax中,自加1之后的地址存在edx,等价于指针向后移一位。之后取了i的值存入edx,与ecx相加得到state+4j+i,被赋予eax中的data值。这段代码大致就是state[j][i]=*data;data++;,循环结束后state中便是标准的状态矩阵了。

返回上一级,轮密钥加addRoundKey()调用的第一个参数是var_18,查看定义,发现它在初始化时被赋值为var_17C,而我们在之前已经分析得到var_17C中有最终的密钥矩阵,那么这一步就是取的其中加密时用的密钥矩阵。

3. addRoundKey()

进入分析

跟前面同理,这里是一个两层循环,内层i外层j都是4次,不多说

看一下loc_40180B

先取到key[j],存入edx中,然后计算3-i的值,再左移3位(等价于×8),8*(3-i)存入ecx,接着edx中的key[j]根据这个值向右移位,结果存入ecx。这一步是在进行转换为字节单位的操作。

我们令局部变量var_4为k,这之后取到了k[i][j]存入eax中,把ecx中的值赋值给eax,综合以上,k中存的就是字节流形式的密钥矩阵。后面的部分与前面类似,取到state[i][j]存入edx中,取到k[i][j]存入eax中(这里有一个自减18H的步骤,目的是将矩阵各位置对齐),最终两者进行异或,轮密钥加的结果存在了edx中。

第一次轮密钥加完毕后,就来到了aesEncrypt()的内层循环,可以看到调用了四个函数:subBytes(),shiftRows(),mixColumns(),addRoundKey(),对应了每一轮中的字节代换,行移位,列混合,轮密钥加这四步,刚刚我们已经分析了轮密钥加,现在来看看前三步。

首先将指向密钥矩阵的指针keyMat后移10H=16位,目的是在下一次轮密钥加时用到W[4]-W[7]。

4. subBytes()

subBytes()函数调用的参数是状态矩阵stateMat,进入看一下

整体来看又是一个两层循环,4*4,只需关注左下角的loc_4018CA

这里是先取了stateMat[i][j]到eax中,后面直接以这个地址作为字节指针在S盒里面寻找对应的值,将其覆盖,最后edx中的指针复位后,指向stateMat[i][j]+j即stateMat[i][j+1],如此循环之后完成字节代换。

5. shiftRows()

shiftRows()函数调用的参数是完成字节代换后的状态矩阵。

这个函数的主体部分是一个四次的循环,定义的局部变量不再展示

loc_40194C由先后几套移位的操作组成,每种操作里面各有四次处理。

如上图标记的第一类操作重复了四次,后面三次以or为结束标志,大致是取到了stateMat[i]的四个字节,然后第一次移位18H=24位,第二次16位,以此类推就将一共32位分四字节存入edx中。

第二类操作如上图所示,var_18+4i取得了edx处的值,可以理解为每次循环时,var_18中存有状态矩阵中的一行,它先被存入了edx,后复制到ebx中,对于循环变量i,先是存入eax,而eax进行了一次左移三位的操作存入ecx,相当于8*i,随后var_18向左移8*i位。接着,类似地,,var_18[i]被复制进edx,8*(4-i)存入ecx,var_18[i]右移位8*(4-i)位,最后将对var_18[i]两个相反方向的移位结果进行或运算,存入edx。

第三类操作跟第一类很类似,这里var_18是已经经过前两步移位操作后得到的状态矩阵中的一行,于是现在要将stateMat按照它来更新。先取到stateMat[i]存入edx中,var_18[i]存入eax,再右移18H=24位,相当于取第一个字节,最后将后者的值赋值给前者,后面的同理,每次右移的位数相差8位,4次操作之后,32位的var_18[i]正好全部复制完毕,行移位操作就完成了。

6. mixColumns()

mixColumns()函数调用的参数是完成行移位之后的状态矩阵,这里面会用到伽罗瓦域上的运算操作。

先进入看:

从30H-21H这里连续16个字节,定义了2311123111233112这样一串数字,而在进行列混合时的实质就是矩阵相乘,规模都是4×4,所以应该把这串数字视作矩阵里面的每个元素。

接下来的是循环结构,因为用的循环变量没有更改,所以看起来比较混乱,但是可以根据箭头的指向能分析出两个4×4的双层循环

先来看第一个循环loc_401B2D

这种结构之前分析过很多次了,先是取到stateMat[i][j]的值存入eax,然后再计算var_8+4i+j即取var_8[i][j]的初始地址存入edx,减去多的18H=24位,最后将eax的值赋值给edx,总的来说就是把状态矩阵存入了一个临时变量var_8。

再看第二个循环loc_401B85

我们把存储着目标相乘矩阵的局部变量var_30命名为M,刚刚的临时变量var_8的值传递给了var_20,命名为temp。这里调用了四次GMul函数,在每次进行调用会进行形参的初始化,拿第一次来说,先取到stateMat[i],赋值给ebx,随后取到temp+j这个地址,存到了eax中,也就是temp[0][j],再转移到edx中去,最后是取到M[i][0]于eax中,temp[0][j]赋值给形参var_3C,M[i][0]赋值给var_40,GMul()函数调用这两个形参进行计算。

7. GMul()

进入GMul()函数分析:

注意到在初始化局部变量阶段,M的值被赋给了var_14,temp被赋给了var_18,另外还有两个变量var_4和var_5都初始为0,后分析得var_4是循环变量i

继续往下走

loc_401A95这里,将M与1进行与运算,相当于判断这个值是否为0,若不为0则先将var_5与temp异或,然后再将temp的最高位(这里的运算是GF(2^8)上的运算,最高位用二进制表示就是80H)与80H异或,所得值赋值给var_C,temp向左移一位。接着判断var_C是否为0,若不为0则将temp与1BH进行异或运算,即模x^8 + x^4 + x^3 + x + 1这个不可约多项式。最后将M右移一位,这里总结起来就是,使用GF(2^8)域上的乘法乘以(00000010)等价于左移1位(低位补0)后,再根据情况同1BH进行异或运算,下图可以表达出这个原理

addRoundKey()已经分析过,那么现在整个轮函数就结束了

8. storeStateArray()

最后一轮结束后调用了storeStateArray()这个函数,调用的参数是var_14和stateMat,倒回去看它的初始化情况:

发现var_14就是aesEncrypt()的第四个参数ct

最后点进这个函数看看:

这个结构就比较简单了,显而易见是一个4×4的两层循环,在左下角的loc_40156E里边,分别取了ct的地址存入eax作为指针,然后取stateMat[i][j]的值赋值给ct,最后ct指针自加1,相当于把矩阵重新排列成字符串,于是加密后的字符串就存储在ct中,aesEncrypt()分析结束。

最后这里就是让明文和密文都自加16字节,开始下一个分组的加密

整个加密过程就是如此

You really know!!!!!

附上这个程序的源码:

#include <stdio.h>
#include <Windows.h>#define BLOCKSIZE 16#define LOAD32H(x, y) \do { (x) = ((unsigned int)((y)[0] & 0xff)<<24) | ((unsigned int)((y)[1] & 0xff)<<16) | \((unsigned int)((y)[2] & 0xff)<<8)  | ((unsigned int)((y)[3] & 0xff));} while(0)#define STORE32H(x, y) \do { (y)[0] = (unsigned char)(((x)>>24) & 0xff); (y)[1] = (unsigned char)(((x)>>16) & 0xff);   \(y)[2] = (unsigned char)(((x)>>8) & 0xff); (y)[3] = (unsigned char)((x) & 0xff); } while(0)/* extract a byte */
#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)/* used for keyExpansion */
#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))#define ROF32(x, n)  (((x) << (n)) | ((x) >> (32-(n))))#define ROR32(x, n)  (((x) >> (n)) | ((x) << (32-(n))))/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
static const unsigned int rcon[10] = {0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};unsigned char S[256] = {0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};unsigned char inv_S[256] = {0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};typedef struct{unsigned int eK[44], dK[44];    // encKey, decKeyint Nr; // 10 rounds
}AesKey;/* copy in[16] to state[4][4] */
int loadStateArray(unsigned char (*state)[4], const unsigned char *in) {int i;int j;for (i = 0; i < 4; ++i) {for (j = 0; j < 4; ++j) {state[j][i] = *in++;}}return 0;
}/* copy state[4][4] to out[16] */
int storeStateArray(unsigned char (*state)[4], unsigned char *out) {int i;int j;for (i = 0; i < 4; ++i) {for (j = 0; j < 4; ++j) {*out++ = state[j][i];}}return 0;
}int keyExpansion(const unsigned char *key, unsigned int keyLen, AesKey *aesKey) {int i;int j;unsigned int *w;unsigned int *v;if (NULL == key || NULL == aesKey){printf("keyExpansion param is NULL\n");return -1;}if (keyLen != 16){printf("keyExpansion keyLen = %d, Not support.\n", keyLen);return -1;}w = aesKey->eK;v = aesKey->dK;/* keyLen is 16 Bytes, generate unsigned int W[44]. *//* W[0-3] */for (i = 0; i < 4; ++i) {LOAD32H(w[i], key + 4*i);}/* W[4-43] */for (i = 0; i < 10; ++i) {w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];w[5] = w[1] ^ w[4];w[6] = w[2] ^ w[5];w[7] = w[3] ^ w[6];w += 4;}w = aesKey->eK+44 - 4;for (j = 0; j < 11; ++j) {for (i = 0; i < 4; ++i) {v[i] = w[i];}w -= 4;v += 4;}return 0;
}int addRoundKey(unsigned char (*state)[4], const unsigned int *key) {int i;int j;unsigned char k[4][4];/* i: row, j: col */for (i = 0; i < 4; ++i) {for (j = 0; j < 4; ++j) {k[i][j] = (unsigned char) BYTE(key[j], 3 - i);  /* copy uint32 key[4] to uint8 k[4][4] */state[i][j] ^= k[i][j];}}return 0;
}int subBytes(unsigned char (*state)[4]) {/* i: row, j: col */int i;int j;for (i = 0; i < 4; ++i) {for (j = 0; j < 4; ++j) {state[i][j] = S[state[i][j]];}}return 0;
}int shiftRows(unsigned char (*state)[4]) {int i;unsigned int block[4] = {0};/* i: row */for (i = 0; i < 4; ++i) {LOAD32H(block[i], state[i]);block[i] = ROF32(block[i], 8*i);STORE32H(block[i], state[i]);}return 0;
}/* Galois Field (256) Multiplication of two Bytes */
unsigned char GMul(unsigned char u, unsigned char v) {int i;int j;int flag;unsigned char p = 0;for (i = 0; i < 8; ++i) {if (u & 0x01) {    //p ^= v;}flag = (v & 0x80);v <<= 1;if (flag) {v ^= 0x1B; /* x^8 + x^4 + x^3 + x + 1 */}u >>= 1;}return p;
}int mixColumns(unsigned char (*state)[4]) {int i;int j;unsigned char tmp[4][4];unsigned char M[4][4] = {{0x02, 0x03, 0x01, 0x01},{0x01, 0x02, 0x03, 0x01},{0x01, 0x01, 0x02, 0x03},{0x03, 0x01, 0x01, 0x02}};/* copy state[4][4] to tmp[4][4] */for (i = 0; i < 4; ++i) {for (j = 0; j < 4; ++j){tmp[i][j] = state[i][j];}}for (i = 0; i < 4; ++i) {for (j = 0; j < 4; ++j) {state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);}}return 0;
}int aesEncrypt(const unsigned char *key, unsigned int keyLen, const unsigned char *pt, unsigned char *ct, unsigned int len) {int i;int j;AesKey aesKey;unsigned char *pos = ct;const unsigned int *rk = aesKey.eK;unsigned char out[BLOCKSIZE] = {0};unsigned char actualKey[16] = {0};unsigned char state[4][4] = {0};if (NULL == key || NULL == pt || NULL == ct){printf("param err.\n");return -1;}if (keyLen > 16){printf("keyLen must be 16.\n");return -1;}if (len % BLOCKSIZE){printf("inLen is invalid.\n");return -1;}memcpy(actualKey, key, keyLen);keyExpansion(actualKey, 16, &aesKey);for (i = 0; i < len; i += BLOCKSIZE) {loadStateArray(state, pt);addRoundKey(state, rk);for (j = 1; j < 10; ++j) {rk += 4;subBytes(state);shiftRows(state);mixColumns(state);addRoundKey(state, rk);}subBytes(state);shiftRows(state);addRoundKey(state, rk+4);storeStateArray(state, pos);pos += BLOCKSIZE;pt += BLOCKSIZE;rk = aesKey.eK;}return 0;
}int main() {int i;int j;const unsigned char key[]="1234567890123456";unsigned char data[40] = "abcdefghijklmnopqrstuvwxyzabcdef";unsigned char cipher[32] = {0xfc,0xad,0x71,0x5b,0xd7,0x3b,0x5c,0xb0,0x48,0x8f,0x84,0x0f,0x3b,0xad,0x78,0x89,0xd0,0xe7,0x09,0xd0,0xff,0xd3,0x8c,0x6d,0xfe,0xc5,0x5c,0xcb,0x9f,0x47,0x5b,0x01};unsigned char ct[32] = {0};printf("Please provide the Flag:");scanf("%s",data);aesEncrypt(key, 16, data, ct, 32);for(i = 0; i < 32; ++i){if (cipher[i] != ct[i]){printf("You really don't know!!!!!\n");break;}}if(i==32){printf("You really know!!!!!\n");}system("pause");return 0;
}

AES算法逆向分析报告相关推荐

  1. AES加密算法逆向分析

    AESEnc逆向分析 1. 观察程序执行流程 打开程序提示输入Flag,随意键入1个值之后程序输出"You really don't know!!!",即Flag错误.已知该程序是 ...

  2. 010 Editor算法逆向分析之编写注册机

    将程序拖入OD,通过字符串搜索定位到核心代码,经过分析,主要是如下图所示的两个关键函数,返回正确的值,才算是注册成功. 1 开始分析,我们点击register按钮,弹出来窗口让我们输入用户名和密码 1 ...

  3. 某影视APP算法逆向分析

    一.抓取数据包 1.请求头有%加十六进制,说以是url编码,先解密一下 GET http://m.mapps.m1905.cn/User/sendVer?request=jYgPer7AuEqdM+D ...

  4. SM4国密算法实现分析

    SM4国密算法实现分析 代码下载请见 上一篇文章 AES算法实现分析 SM4的说明(pdf):http://download.csdn.net/detail/leechiyang/5008528 算法 ...

  5. js aes加密_某高考咨询网js逆向分析笔记

    一.某高考资讯网逆向分析 某网站的js加密分析,安全签名signsafe + HmacSHA1 + AES 一年前分析过网站数据还没有加密,最近需要获取新的数据发现原先的爬虫失效,请求和响应都经过加密 ...

  6. 58同城AES签名接口逆向分析

    背景:需要获取58同城上面发布的职位信息,其中的包括职位的招聘要求,薪资福利,公司的信息,招聘者的联系方式.(中级爬虫的难度系数) 职位详情页分析   某个职位详情页的链接 某个职位的链接 https ...

  7. JS逆向--PyExecJS基本用法--网易云音乐逆向思路,node.js安装教程,逆向思路,逆向分析,加密机制,RSA,AES加密算法,加密算法啊破解,js引擎,定位数据包,分析栈结构,无痕窗口

    文章目录 前言 一.JS逆向以及PyExecJS模块介绍 1.JS逆向 2.PyEecJS 二.使用步骤 1.环境安装 安装PyExecJS模块 安装node.js开发环境(官网链接 https:// ...

  8. c语言将十进制转化为二进制算法_base64算法初探即逆向分析

    算法分析 虽说base64严格意义上来说并不能算是加密算法,但的确应用方面来说还算是比较广,在CTF的算法逆向中Base系列算是也比较常见的,萌新刚开始学算法,就以base64为例,对该算法进行一个简 ...

  9. 记一次js文件AES加密的key与iv逆向分析

    文章目录 前言 一.AES算法介绍 二.js文件代码 1.util.js文件 2.aes.js文件 三.代码分析 1.分析加密调用及过程 2. 分析e(key),n(iv)生成的过程 总结 前言 最近 ...

最新文章

  1. 书单 | 计算机视觉的修炼秘笈
  2. python【力扣LeetCode算法题库】7- 整数反转
  3. 材料模拟python_用Python模拟无限生成器
  4. 系统架构设计师教程学习随笔 (计算机与网络基础知识--操作系统基础知识)
  5. 农民思考互联网时代农民的未来
  6. 机器学习-cs229-线性回归-梯度下降法
  7. 使用ueditor实现多图片上传案例——截取字符串层Util(SubString_text)
  8. 单片机 c语言 概念题,(C语言版)单片机复习题.doc
  9. git clone报错:Permission denied (publickey). fatal: Could not read from remote repository...
  10. MySQL助手_java 8.0Mysql 助手类
  11. linux 访问驱动器_Linux上的访问控制列表和外部驱动器:您需要了解的内容
  12. c++ tcp 服务器和客户端例子
  13. 在线HTML压缩工具
  14. Kafka 副本leader选举
  15. 第二季-专题20-移植bootm命令启动内核
  16. java 正则车牌_javascript匹配车牌号正则表达式
  17. 低功耗服务器cpu性能排行,电脑CPU天梯图性能排行榜 CPU性能天梯图2018年6月最新版...
  18. 大规模定制有哪些标志性的特点
  19. Networking Named Content 全文翻译(转)
  20. js中的json和ascii转换

热门文章

  1. 计算机校园网网络工程论文,【网络工程论文】网络工程研究及校园网设计
  2. c语言标准库详解(十二):非局部跳转setjmp.h
  3. C语言中全局变量和局部变量,内部函数和外部函数的区别
  4. 给浏览器设置一个图片背景/主题
  5. SQL Server连接表
  6. 利用阿里云短信验证码登录
  7. 1.3 模拟/dp|大话移动通信
  8. 怎么在网页中播放视频之一:HTML5视频嵌入
  9. 《通信原理》多径衰落信道仿真2
  10. Matlab绘制三维曲线(plot3)和三维图形(mesh surf)