首页
社区
课程
招聘
[原创]看雪 2022·KCTF 春季赛 > 第三题 石像病毒 by 心学
发表于: 2022-5-16 23:22 12733

[原创]看雪 2022·KCTF 春季赛 > 第三题 石像病毒 by 心学

htg 活跃值
4
2022-5-16 23:22
12733

日期:2022-05-14
CTF:htg
题目:看雪 2022·KCTF 春季赛 > 第三题 石像病毒

工具:IDA
要点:算法 MD5、AES

MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
将任意长度的明文,转化为128位的哈希值。

明文字符串转储为字节序列,默认编码格式为utf-8

字节序列长度整除512,如余数不等于448,则在后面补充1个1和多个0,直至整除512的余数为448

接着在后面添加字节序列的长度(二进制位数)

接着在后面补0,直至长度被512整除

设置后续循环计算的初始值

每次处理512个字节,直至处理完成,即循环次数为:预处理后的字节序列长度 / 512 的结果

四种函数

针对四种函数,定义与其对应的四个

以一个512字节序列的过程如下:

分组后的数组长度为 16 ,每个单元长度为32,即4个字节
分别进行16次FF、GG、HH、II运算,每次运行时,ABCD的参数进行右移替换
以16次FF举例,分别是:

实际上是将int类型的值在内存中字节序列原位输出。
比如:0x01234567,其在内存中存储序列为 67452301,那么反转之后为'67452301',将四个字符串合并输出,即为 md5 hash

初始值是识别算法的特征
两处扩充(448、512)
有4种基本操作的函数
MD5_K_Order有0x64,存储sin值
主循环内有64次子循环

算法详解:https://blog.csdn.net/u010037269/article/details/123863979
主要概念:

AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。
密钥的长度可以使用128位(10轮)、192位(12轮)或256位(14轮)。密钥的长度不同,推荐加密轮数也不同。
本题是AES-128,有11轮操作
AES的处理单位是字节
图片引自https://blog.csdn.net/u010037269/article/details/123863979

128位的输入明文分组P被分成16个字节

128位密钥也是用字节为单位的矩阵表示,矩阵的每一列被称为1个32位比特字。
通过密钥编排函数该密钥矩阵被扩展成一个44个字组成的序列W[0],W[1], … ,W[43],该序列的前4个元素W[0],W[1],W[2],W[3]是原始密钥,用于加密运算中的初始密钥加(下面介绍);后面40个字分为10组,每组4个字(128比特)分别用于10轮加密运算中的轮密钥加。

加密的第1轮到第9轮的轮函数一样,包括4个操作:字节代换、行位移、列混合和轮密钥加。最后一轮迭代不执行列混合。另外,在第一轮迭代之前,先将明文和原始密钥进行一次异或加密操作。

AES的字节代换其实就是一个简单的查表操作。AES定义了一个S盒和一个逆S盒。

行移位是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节

列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵

轮密钥加是将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作

分组密码有五种工作体制:1.电码本模式(Electronic Codebook Book (ECB));2.密码分组链接模式(Cipher Block Chaining (CBC));3.计算器模式(Counter (CTR));4.密码反馈模式(Cipher FeedBack (CFB));5.输出反馈模式(Output FeedBack (OFB))。

双击运行程序,出现一个cmd窗口,随便输入信息,程序自动关闭
打开cmd,将程序拖入进去,回车,随便输入信息,显示 NO

用IDA打开,分析之后,找到main函数,具体代码如下:

主程序架构不算复杂,主要结构如下:

此时,要观察汇编代码,才能看出程序修改了哪些内容。
我们可以找到上述代码位置,异常处理有三块:
.text:000628D3 try
.text:00062901 filter【如果打开graphic,它在右上角】
.text:000628FB except

代码的编排顺序是:try->except->filter
代码的执行顺序却是: try->filter->except
可以进行动态调试跟踪确认,另外在IDA动态调试过程中需要注意:
弹出:EB28F0:The instruction at 0xEB28F0 referenced memory at 0x0. The memory could not be written -> 00000000(exc.code c0000005,tid 11380)
这是触发了内存不可访问异常
应对:去 IDA Debugger ---> Debugger Options ,打开 Debugger Setup 窗口
点击左下角 Edit exceptions
找到 第一项 code C0000005,去掉 Suspend program ,选中 Pass to applicaiton;勾选 log 就行(在控制台里可以看到信息)
也就是说,不让IDA接管这个异常,让应用程序自身的机制去处理。
后续出现的反调试,都可以按照这个来。

strcpy((char *)&v8[9], "Enj0y_1t_4_fuuuN");

自此,主程序代码分析完成,基本框架清楚,主要集中在两个函数sub_61109和sub_61104

主程序代码如下

在这里有一处主动触发异常,修改MD5算法,直接查看伪代码是看不来来的

而查看 sub_62CE0 的汇编代码 graphic结构,会发现右侧有一个异常filter代码

在 __except filter // owned by 62D13 内有一个方法call sub_610FA,他会对 对 MD5_K_order[ecx], 0D4AF3085h 进行修改

这里面出现了 MD5_K_order ,建议加载 FindCrypt3 对代码进行算法分析
[FindCrypt3] - 0xEB2E35: found sparse constants MD5_initState for MD4/MD5/RMD128
[FindCrypt3] - 0xEB8B30: found const array AES_Rijndael_rcon (used in AES/Rijndael), size = 10, elsize = 4
[FindCrypt3] - 0xEBB000: found const array Rijndael_sbox (used in Rijndael), size = 256, elsize = 1
[FindCrypt3] - 0xEBB100: found const array Rijndael_inv_sbox (used in Rijndael), size = 256, elsize = 1
[FindCrypt3] - 0xEBB298: found const array MD5_K_order (used in MD5), size = 64, elsize = 4
[FindCrypt3] - 0xEBB298: found sparse constants MD5_Transform for MD5
[FindCrypt3] - Found 6 known constant arrays in total.
解决办法就是:找一段MD5的算法代码,在对应位置进行修改,以获取正确的值。
也可以直接在IDA调试的时候,拷贝出来
2F65B1FF31ED86D09A285C0F4048059D

在IDA里,采用graphics模式,我们观察到 右侧有异常filter代码,证明里面存在try代码,猜测可能存在主动出发异常,在阅读代码的时候,发现有一段代码不连续

.text:00061685 dd 0FFFFFFFEh
触发了 An attempt was made to execute an illegal instruction (exc.code c000001d,tid 11028)
进而会将代码跳转到 __except filter // owned by 6167A

我们发现右侧还有两个,证明还存在2处触发异常的代码

这是最后一个

以上三处触发异常的filter,都是执行了一个方法 call sub_6108C
这个方法具体做了啥?对AES合资进行了交换,71 与 A3 进行了互换
我们查AES原始的盒子【71】=A3;【A3】=0A
那么我们构造逆盒子的时候,就应该将 0A 和 A3 进行替换

sub_61AE0 无法F5,直接在graphics里观看,发现一处异常处理代码

其关联如下:这是 try 代码,触发异常之后,这段代码会回滚

异常filter执行之后的代码exception

我们对比分析只有,发现两端代码有很多相似之处,但是有一个循环左移与循环右移的区别
那就是说,这段代码对 行位移 的关键操作进行了变换,循环左移 改为了循环右移

里面有四个 sub_61118 的操作,继续跟进下去,发现这个函数做了一些改变,对Buf2进行了修改

sub_EB1D50看伪代码,的确不能发现问题,需要回到 Graphics里看,有一段异常代码处理

这个异常代码做了什么?

Buf2就是用户SN经过AES加密之后的密文:
577CF56D56967745B0BDA1C789A5ABDCF4F24BFEBEF5F55C4D30420F2B3BE6CB
这个方法是将两个字节进行了替换,上面是经过替换之后的内容,而最初的内容是
577CF56D56967745B0BDA1C789A5ABDCF2F44BFEBEF5F55C4D30420F2B3BE6CB
以上代码全部分析完成

key = gMD5("Enj0y_1t_4_fuuuN")

encryptContent = gAES(key,SN)

encryptContent == 577CF56D56967745B0BDA1C789A5ABDCF2F44BFEBEF5F55C4D30420F2B3BE6CB

待补充

 
A = 0x67452301
B = 0xEFCDAB89
C = 0x98BADCFE
D = 0x10325476
A = 0x67452301
B = 0xEFCDAB89
C = 0x98BADCFE
D = 0x10325476
F(X, Y, Z) = (X&Y) | ((~X) & Z)
G(X, Y, Z) = (X&Z) | (Y & (~Z))
H(X, Y, Z) = X^Y^Z
I(X, Y, Z) = Y^(X|(~Z))
F(X, Y, Z) = (X&Y) | ((~X) & Z)
G(X, Y, Z) = (X&Z) | (Y & (~Z))
H(X, Y, Z) = X^Y^Z
I(X, Y, Z) = Y^(X|(~Z))
FF(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(F(b,c,d)+Mi+tj)<<< s)
GG(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(G(b,c,d)+Mi+tj)<<< s)
HH(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(H(b,c,d)+Mi+tj)<<< s)
II(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(I(b,c,d)+Mi+tj)<<< s)
1)其中Mi表示消息的第i个子分组(从015,共16个)
2)<<< s表示循环左移s位
3)常数tj为:在第j步中,tj是4294967296*abs(sin(j))的整数部分,i的单位是弧度。(4294967296232次方)亦可用 0x100000000UL * abs(sin((double)j)) 计算
4)i和j 都是针对一次主循环而言的,下一个主循环开始后,i和j将重置。
5)a,b,c,d 就是每一步中的 A,B,C,D
FF(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(F(b,c,d)+Mi+tj)<<< s)
GG(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(G(b,c,d)+Mi+tj)<<< s)
HH(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(H(b,c,d)+Mi+tj)<<< s)
II(a,b,c,d,Mi,s,tj)  表示 a=b+((a+(I(b,c,d)+Mi+tj)<<< s)
1)其中Mi表示消息的第i个子分组(从015,共16个)
2)<<< s表示循环左移s位
3)常数tj为:在第j步中,tj是4294967296*abs(sin(j))的整数部分,i的单位是弧度。(4294967296232次方)亦可用 0x100000000UL * abs(sin((double)j)) 计算
4)i和j 都是针对一次主循环而言的,下一个主循环开始后,i和j将重置。
5)a,b,c,d 就是每一步中的 A,B,C,D
a=FF(a,b,c,d,M0,7,0xd76aa478)
b=FF(d,a,b,c,M1,12,0xe8c7b756)
c=FF(c,d,a,b,M2,17,0x242070db)
d=FF(b,c,d,a,M3,22,0xc1bdceee)
a=FF(a,b,c,d,M4,7,0xf57c0faf)
b=FF(d,a,b,c,M5,12,0x4787c62a)
c=FF(c,d,a,b,M6,17,0xa8304613)
d=FF(b,c,d,a,M7,22,0xfd469501)
a=FF(a,b,c,d,M8,7,0x698098d8)
b=FF(d,a,b,c,M9,12,0x8b44f7af)
c=FF(c,d,a,b,M10,17,0xffff5bb1)
d=FF(b,c,d,a,M11,22,0x895cd7be)
a=FF(a,b,c,d,M12,7,0x6b901122)
b=FF(d,a,b,c,M13,12,0xfd987193)
c=FF(c,d,a,b,M14,17,0xa679438e)
d=FF(b,c,d,a,M15,22,0x49b40821)
a=FF(a,b,c,d,M0,7,0xd76aa478)
b=FF(d,a,b,c,M1,12,0xe8c7b756)
c=FF(c,d,a,b,M2,17,0x242070db)
d=FF(b,c,d,a,M3,22,0xc1bdceee)
a=FF(a,b,c,d,M4,7,0xf57c0faf)
b=FF(d,a,b,c,M5,12,0x4787c62a)
c=FF(c,d,a,b,M6,17,0xa8304613)
d=FF(b,c,d,a,M7,22,0xfd469501)
a=FF(a,b,c,d,M8,7,0x698098d8)
b=FF(d,a,b,c,M9,12,0x8b44f7af)
c=FF(c,d,a,b,M10,17,0xffff5bb1)
d=FF(b,c,d,a,M11,22,0x895cd7be)
a=FF(a,b,c,d,M12,7,0x6b901122)
b=FF(d,a,b,c,M13,12,0xfd987193)
c=FF(c,d,a,b,M14,17,0xa679438e)
d=FF(b,c,d,a,M15,22,0x49b40821)
#来源:https://github.com/timvandermeij/md5.py/blob/master/md5.py
"""
The implementation of the MD5 algorithm is based on the original RFC at
https://www.ietf.org/rfc/rfc1321.txt and contains optimizations from
https://en.wikipedia.org/wiki/MD5.
"""
 
import struct
from enum import Enum
from math import (floor,sin,)
import os
import binascii
 
#pip install bitarray
from bitarray import bitarray
 
class MD5Buffer(Enum):
    A = 0x67452301
    B = 0xEFCDAB89
    C = 0x98BADCFE
    D = 0x10325476
 
class MD5(object):
    _string = None
    _buffers = {
        MD5Buffer.A: None,
        MD5Buffer.B: None,
        MD5Buffer.C: None,
        MD5Buffer.D: None,
    }
 
    @classmethod
    def hash(cls, string):
        cls._string = string
 
        preprocessed_bit_array = cls._step_2(cls._step_1())
        cls._step_3()
        cls._step_4(preprocessed_bit_array)
        return cls._step_5()
 
    @classmethod
    def _step_1(cls):
        # Convert the string to a bit array.
        # 将 string 转换为 bit_array (比特序列)
        bit_array = bitarray(endian="big")
        # big 大端存储就是将字符串原为转为字节序列,比如字符串'01234567'转到内存中存储(地址由低到高)就是 30 31 32 33 34 35 36 37,进一步将该字节序列转为一个int型,其值就是 0x
        bit_array.frombytes(cls._string.encode("utf-8"))
        #print("bit_array:{}".format(bit_array))
        #os.system("pause")
        # Pad the string with a 1 bit and as many 0 bits required such that
        # the length of the bit array becomes congruent to 448 modulo 512.
        # Note that padding is always performed, even if the string's bit
        # length is already conguent to 448 modulo 512, which leads to a
        # new 512-bit message block.
        # 在 bit_array 比特序列后面添加1个1和多个0,直至能够被 512 整数之后余数为 448
        bit_array.append(1)
        while len(bit_array) % 512 != 448:
            bit_array.append(0)
 
        # For the remainder of the MD5 algorithm, all values are in
        # little endian, so transform the bit array to little endian.
        # 转换 bit_array 之后的存储方式
        bit_array = bitarray(bit_array, endian="little")
        #print("bit_array:{}".format(bit_array))
        #os.system("pause")
        return bit_array
 
    @classmethod
    def _step_2(cls, step_1_result):
        # Extend the result from step 1 with a 64-bit little endian
        # representation of the original message length (modulo 2^64).
        # 在 bit_array 之后添加 原始字符串的长度(比特位数)整除 2^64 的 余数
        # MD5 处理的长度最高限定为 2^61-1 个字节,64个比特位
        length = (len(cls._string) * 8) % pow(2, 64)
        #print("length:{}={}={}".format(length,hex(length),bin(length)))
        #os.system("pause")
 
        #下面两句,直接按【比特位】进行小端存放,    
        #length_bit_array = bitarray(endian="little")
        #length_bit_array.frombytes(struct.pack("<Q", length))#按照QWORD类型存储,即2^64
 
        #替换代码:实际应该是按照【字节】小端存放(CTF题目是字节小端存放)
        length_byte_array = struct.pack("<Q", length)
        #print("length_byte_array:{}".format(length_byte_array))
        #os.system("pause")       
        length_bit_array = bitarray(endian="big")
        length_bit_array.frombytes(length_byte_array)       
        #print("length_bit_array:{}".format(length_bit_array))
        #os.system("pause")
 
        result = step_1_result.copy()
        result.extend(length_bit_array)
        print("result:{}".format(result))
        os.system("pause")
 
        return result
 
    @classmethod
    def _step_3(cls):
        # Initialize the buffers to their default values.
        for buffer_type in cls._buffers.keys():
            cls._buffers[buffer_type] = buffer_type.value
 
    @classmethod
    def _step_4(cls, step_2_result):
        # Define the four auxiliary functions that produce one 32-bit word.
        # 定义四种处理函数
        F = lambda x, y, z: (x & y) | (~x & z)
        G = lambda x, y, z: (x & z) | (y & ~z)
        H = lambda x, y, z: x ^ y ^ z
        I = lambda x, y, z: y ^ (x | ~z)
 
        # Define the left rotation function, which rotates `x` left `n` bits.
        # 定义 循环左移操作操作
        rotate_left = lambda x, n: (x << n) | (x >> (32 - n))
 
        # Define a function for modular addition.
        # 定义 基于 int32(2^32)的模加法
        modular_add = lambda a, b: (a + b) % pow(2, 32)
 
        # Compute the T table from the sine function. Note that the
        # RFC starts at index 1, but we start at index 0.
        # 建议长度为64的 Sin表,即 0x100000000UL*abs(sin(j))的整数部分,其中j从1开始。
        T = [floor(pow(2, 32) * abs(sin(i + 1))) for i in range(64)]
 
        #更改T[0x2A]:这个就是 CTF题目中更改的地方。表 T 与 表 MD5_K_order 相同
        T[0x2A]=0xD4AF3085
 
        # The total number of 32-bit words to process, N, is always a
        # multiple of 16.
        #
        N = len(step_2_result) // 32
 
        # Process chunks of 512 bits.
        # 每次处理 512 位二进制序列
        for chunk_index in range(N // 16):
            # Break the chunk into 16 words of 32 bits in list X.
            # 分成16个在分组,每个单元是 32 位,然后转换成 int
 
            # chunk_index 是一个大循环的索引号,每次主循环处理512个字节
            start = chunk_index * 512
            X = [step_2_result[start + (x * 32) : start + (x * 32) + 32] for x in range(16)]
            #print("X[0]:{}".format(X[0]))
            # Convert the `bitarray` objects to integers.
            '''
            #原始代码:按【比特位】小端进行处理   
            X = [int.from_bytes(word.tobytes(), byteorder="little") for word in X]
            '''
 
            #按【字节】小端进行处理
            #按大端存放,也就是解析时位置保存不变
            X = [bitarray(word, endian="big") for word in X]
            print("X[0]:{}".format(X[0]))
            #转为字节序列:这一步就是要保持比特位顺序原位不变,直接转换为 字节序列
            #X[0]:bitarray('01000101011011100110101000110000')
            #X[0]:b'Enj0'      4   5   6   E   6   A   3   0     
            #X[0]:306A6E45
            X = [word.tobytes() for word in X]           
            print("X[0]:{}".format(X[0]))
            #按【字节】小端存放,也就是\x45\x6E\x6A\x30--->0x306A6E45
            X = [int.from_bytes(word, byteorder="little") for word in X]
            print("X[0]:{:02X}".format(X[0]))
            #os.system("pause")
 
            # Make shorthands for the buffers A, B, C and D.
            # 定义A B C D的初值:每次处理512位数据前,均要将ABCD初始化
            A = cls._buffers[MD5Buffer.A]
            B = cls._buffers[MD5Buffer.B]
            C = cls._buffers[MD5Buffer.C]
            D = cls._buffers[MD5Buffer.D]
 
            # Execute the four rounds with 16 operations each.
            # 执行 64 次循环,每16次为一组,依次是 F、G、H、I各执行16次
            for i in range(4 * 16):
                if 0 <= i <= 15:
                    k = i
                    s = [7, 12, 17, 22]
                    temp = F(B, C, D)
                elif 16 <= i <= 31:
                    k = ((5 * i) + 1) % 16
                    s = [5, 9, 14, 20]
                    temp = G(B, C, D)
                elif 32 <= i <= 47:
                    k = ((3 * i) + 5) % 16
                    s = [4, 11, 16, 23]
                    temp = H(B, C, D)
                elif 48 <= i <= 63:
                    k = (7 * i) % 16
                    s = [6, 10, 15, 21]
                    temp = I(B, C, D)
 
                # The MD5 algorithm uses modular addition. Note that we need a
                # temporary variable here. If we would put the result in `A`, then
                # the expression `A = D` below would overwrite it. We also cannot
                # move `A = D` lower because the original `D` would already have
                # been overwritten by the `D = C` expression.
                # 依次执行模加、模加、模加、循环左移、模加运算
                # 对应的是:b+((a+(F(b,c,d)+Mi+tj)<<< s)
                temp = modular_add(temp, X[k])      #F(b,c,d)+Mi
                temp = modular_add(temp, T[i])      #F(b,c,d)+Mi+tj
                temp = modular_add(temp, A)         #a+(F(b,c,d)+Mi+tj
                temp = rotate_left(temp, s[i % 4])  #【上一步结果】<<<s
                temp = modular_add(temp, B)         #b+【上一步结果】
 
                # Swap the registers for the next operation.
                # 循环交换 A、B、C、D的值。对照下面的例子
                # a=FF(a,b,c,d,M0,7,0xd76aa478)
                # b=FF(d,a,b,c,M1,12,0xe8c7b756)
                A = D
                D = C
                C = B
                B = temp
 
            #循环已经结果
 
            # Update the buffers with the results from this chunk.
            # 更新 A、B、C、D的值,作为下一个512位数据处理的初始值。
            cls._buffers[MD5Buffer.A] = modular_add(cls._buffers[MD5Buffer.A], A)
            cls._buffers[MD5Buffer.B] = modular_add(cls._buffers[MD5Buffer.B], B)
            cls._buffers[MD5Buffer.C] = modular_add(cls._buffers[MD5Buffer.C], C)
            cls._buffers[MD5Buffer.D] = modular_add(cls._buffers[MD5Buffer.D], D)
 
    @classmethod
    def _step_5(cls):
        # Convert the buffers to little-endian.
        # 将 A、B、C、D 值转换成小端存放
        A = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.A]))[0]
        B = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.B]))[0]
        C = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.C]))[0]
        D = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.D]))[0]
 
        # Output the buffers in lower-case hexadecimal format.
        # 输出结果
        return f"{format(A, '08x')}{format(B, '08x')}{format(C, '08x')}{format(D, '08x')}"
 
#测试
strInter = "Enj0y_1t_4_fuuuN"
md5Value = MD5.hash(strInter).upper()
print("md5Value:{}".format(md5Value))
#来源:https://github.com/timvandermeij/md5.py/blob/master/md5.py
"""
The implementation of the MD5 algorithm is based on the original RFC at
https://www.ietf.org/rfc/rfc1321.txt and contains optimizations from
https://en.wikipedia.org/wiki/MD5.
"""
 
import struct
from enum import Enum
from math import (floor,sin,)
import os
import binascii
 
#pip install bitarray
from bitarray import bitarray
 
class MD5Buffer(Enum):
    A = 0x67452301
    B = 0xEFCDAB89
    C = 0x98BADCFE
    D = 0x10325476
 
class MD5(object):
    _string = None
    _buffers = {
        MD5Buffer.A: None,
        MD5Buffer.B: None,
        MD5Buffer.C: None,
        MD5Buffer.D: None,
    }
 
    @classmethod
    def hash(cls, string):
        cls._string = string
 
        preprocessed_bit_array = cls._step_2(cls._step_1())
        cls._step_3()
        cls._step_4(preprocessed_bit_array)
        return cls._step_5()
 
    @classmethod
    def _step_1(cls):
        # Convert the string to a bit array.
        # 将 string 转换为 bit_array (比特序列)
        bit_array = bitarray(endian="big")
        # big 大端存储就是将字符串原为转为字节序列,比如字符串'01234567'转到内存中存储(地址由低到高)就是 30 31 32 33 34 35 36 37,进一步将该字节序列转为一个int型,其值就是 0x
        bit_array.frombytes(cls._string.encode("utf-8"))
        #print("bit_array:{}".format(bit_array))
        #os.system("pause")
        # Pad the string with a 1 bit and as many 0 bits required such that
        # the length of the bit array becomes congruent to 448 modulo 512.
        # Note that padding is always performed, even if the string's bit
        # length is already conguent to 448 modulo 512, which leads to a
        # new 512-bit message block.
        # 在 bit_array 比特序列后面添加1个1和多个0,直至能够被 512 整数之后余数为 448
        bit_array.append(1)
        while len(bit_array) % 512 != 448:
            bit_array.append(0)
 
        # For the remainder of the MD5 algorithm, all values are in
        # little endian, so transform the bit array to little endian.
        # 转换 bit_array 之后的存储方式
        bit_array = bitarray(bit_array, endian="little")
        #print("bit_array:{}".format(bit_array))
        #os.system("pause")
        return bit_array
 
    @classmethod
    def _step_2(cls, step_1_result):
        # Extend the result from step 1 with a 64-bit little endian
        # representation of the original message length (modulo 2^64).
        # 在 bit_array 之后添加 原始字符串的长度(比特位数)整除 2^64 的 余数
        # MD5 处理的长度最高限定为 2^61-1 个字节,64个比特位
        length = (len(cls._string) * 8) % pow(2, 64)
        #print("length:{}={}={}".format(length,hex(length),bin(length)))
        #os.system("pause")
 
        #下面两句,直接按【比特位】进行小端存放,    
        #length_bit_array = bitarray(endian="little")
        #length_bit_array.frombytes(struct.pack("<Q", length))#按照QWORD类型存储,即2^64
 
        #替换代码:实际应该是按照【字节】小端存放(CTF题目是字节小端存放)
        length_byte_array = struct.pack("<Q", length)
        #print("length_byte_array:{}".format(length_byte_array))
        #os.system("pause")       
        length_bit_array = bitarray(endian="big")
        length_bit_array.frombytes(length_byte_array)       
        #print("length_bit_array:{}".format(length_bit_array))
        #os.system("pause")
 
        result = step_1_result.copy()
        result.extend(length_bit_array)
        print("result:{}".format(result))
        os.system("pause")
 
        return result
 
    @classmethod
    def _step_3(cls):
        # Initialize the buffers to their default values.
        for buffer_type in cls._buffers.keys():
            cls._buffers[buffer_type] = buffer_type.value
 
    @classmethod
    def _step_4(cls, step_2_result):
        # Define the four auxiliary functions that produce one 32-bit word.
        # 定义四种处理函数
        F = lambda x, y, z: (x & y) | (~x & z)
        G = lambda x, y, z: (x & z) | (y & ~z)
        H = lambda x, y, z: x ^ y ^ z
        I = lambda x, y, z: y ^ (x | ~z)
 
        # Define the left rotation function, which rotates `x` left `n` bits.
        # 定义 循环左移操作操作
        rotate_left = lambda x, n: (x << n) | (x >> (32 - n))
 
        # Define a function for modular addition.
        # 定义 基于 int32(2^32)的模加法
        modular_add = lambda a, b: (a + b) % pow(2, 32)
 
        # Compute the T table from the sine function. Note that the
        # RFC starts at index 1, but we start at index 0.
        # 建议长度为64的 Sin表,即 0x100000000UL*abs(sin(j))的整数部分,其中j从1开始。
        T = [floor(pow(2, 32) * abs(sin(i + 1))) for i in range(64)]
 
        #更改T[0x2A]:这个就是 CTF题目中更改的地方。表 T 与 表 MD5_K_order 相同
        T[0x2A]=0xD4AF3085
 
        # The total number of 32-bit words to process, N, is always a
        # multiple of 16.
        #
        N = len(step_2_result) // 32
 
        # Process chunks of 512 bits.
        # 每次处理 512 位二进制序列
        for chunk_index in range(N // 16):
            # Break the chunk into 16 words of 32 bits in list X.
            # 分成16个在分组,每个单元是 32 位,然后转换成 int
 
            # chunk_index 是一个大循环的索引号,每次主循环处理512个字节
            start = chunk_index * 512
            X = [step_2_result[start + (x * 32) : start + (x * 32) + 32] for x in range(16)]
            #print("X[0]:{}".format(X[0]))
            # Convert the `bitarray` objects to integers.
            '''
            #原始代码:按【比特位】小端进行处理   
            X = [int.from_bytes(word.tobytes(), byteorder="little") for word in X]
            '''
 
            #按【字节】小端进行处理
            #按大端存放,也就是解析时位置保存不变
            X = [bitarray(word, endian="big") for word in X]
            print("X[0]:{}".format(X[0]))
            #转为字节序列:这一步就是要保持比特位顺序原位不变,直接转换为 字节序列
            #X[0]:bitarray('01000101011011100110101000110000')
            #X[0]:b'Enj0'      4   5   6   E   6   A   3   0     
            #X[0]:306A6E45
            X = [word.tobytes() for word in X]           
            print("X[0]:{}".format(X[0]))
            #按【字节】小端存放,也就是\x45\x6E\x6A\x30--->0x306A6E45
            X = [int.from_bytes(word, byteorder="little") for word in X]
            print("X[0]:{:02X}".format(X[0]))
            #os.system("pause")
 
            # Make shorthands for the buffers A, B, C and D.
            # 定义A B C D的初值:每次处理512位数据前,均要将ABCD初始化
            A = cls._buffers[MD5Buffer.A]
            B = cls._buffers[MD5Buffer.B]
            C = cls._buffers[MD5Buffer.C]
            D = cls._buffers[MD5Buffer.D]
 
            # Execute the four rounds with 16 operations each.
            # 执行 64 次循环,每16次为一组,依次是 F、G、H、I各执行16次
            for i in range(4 * 16):
                if 0 <= i <= 15:
                    k = i
                    s = [7, 12, 17, 22]
                    temp = F(B, C, D)
                elif 16 <= i <= 31:
                    k = ((5 * i) + 1) % 16
                    s = [5, 9, 14, 20]
                    temp = G(B, C, D)
                elif 32 <= i <= 47:
                    k = ((3 * i) + 5) % 16
                    s = [4, 11, 16, 23]
                    temp = H(B, C, D)
                elif 48 <= i <= 63:
                    k = (7 * i) % 16
                    s = [6, 10, 15, 21]
                    temp = I(B, C, D)
 
                # The MD5 algorithm uses modular addition. Note that we need a
                # temporary variable here. If we would put the result in `A`, then
                # the expression `A = D` below would overwrite it. We also cannot
                # move `A = D` lower because the original `D` would already have
                # been overwritten by the `D = C` expression.
                # 依次执行模加、模加、模加、循环左移、模加运算
                # 对应的是:b+((a+(F(b,c,d)+Mi+tj)<<< s)
                temp = modular_add(temp, X[k])      #F(b,c,d)+Mi
                temp = modular_add(temp, T[i])      #F(b,c,d)+Mi+tj
                temp = modular_add(temp, A)         #a+(F(b,c,d)+Mi+tj
                temp = rotate_left(temp, s[i % 4])  #【上一步结果】<<<s
                temp = modular_add(temp, B)         #b+【上一步结果】
 
                # Swap the registers for the next operation.
                # 循环交换 A、B、C、D的值。对照下面的例子
                # a=FF(a,b,c,d,M0,7,0xd76aa478)
                # b=FF(d,a,b,c,M1,12,0xe8c7b756)
                A = D
                D = C
                C = B
                B = temp
 
            #循环已经结果
 
            # Update the buffers with the results from this chunk.
            # 更新 A、B、C、D的值,作为下一个512位数据处理的初始值。
            cls._buffers[MD5Buffer.A] = modular_add(cls._buffers[MD5Buffer.A], A)
            cls._buffers[MD5Buffer.B] = modular_add(cls._buffers[MD5Buffer.B], B)
            cls._buffers[MD5Buffer.C] = modular_add(cls._buffers[MD5Buffer.C], C)
            cls._buffers[MD5Buffer.D] = modular_add(cls._buffers[MD5Buffer.D], D)
 
    @classmethod
    def _step_5(cls):
        # Convert the buffers to little-endian.
        # 将 A、B、C、D 值转换成小端存放
        A = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.A]))[0]
        B = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.B]))[0]
        C = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.C]))[0]
        D = struct.unpack("<I", struct.pack(">I", cls._buffers[MD5Buffer.D]))[0]
 
        # Output the buffers in lower-case hexadecimal format.
        # 输出结果
        return f"{format(A, '08x')}{format(B, '08x')}{format(C, '08x')}{format(D, '08x')}"
 
#测试
strInter = "Enj0y_1t_4_fuuuN"
md5Value = MD5.hash(strInter).upper()
print("md5Value:{}".format(md5Value))
#来源:https://github.com/heartingrass/AES-Python/blob/master/aes.py
#!/usr/bin/env python
#根据题目的加密方式做了修改
import os
"""
    Copyright (C) 2012 Bo Zhu http://about.bozhu.me
    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
"""
 
#定义:是否执行题目修改了原AES的算法
flag = True
#flag = False
 
Sbox = [
    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,
]
 
InvSbox = [
    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,
]
 
#题目更改了S盒子,那么就更改对应的逆盒子
#Python错误集锦:TypeError: ‘tuple’ object does not support item assignment
#解决方法:
#1、tuple不能修改元素,无解。如果要修改元素,改用list类型。
if flag :
    valueA = Sbox[0x71]
    valueB = Sbox[0xA3]
    #更改盒子
    Sbox[0x71] ^= Sbox[0xA3]
    Sbox[0xA3] ^= Sbox[0x71]
    Sbox[0x71] ^= Sbox[0xA3]
    #更改逆盒子
    InvSbox[valueA] ^= InvSbox[valueB]
    InvSbox[valueB] ^= InvSbox[valueA]
    InvSbox[valueA] ^= InvSbox[valueB]
    print("Sbox[0x71]:{:02X}".format(Sbox[0x71]))
    print("Sbox[0xA3]:{:02X}".format(Sbox[0xA3]))#结果错了。。。
 
 
# learnt from http://cs.ucsb.edu/~koc/cs178/projects/JT/aes.c
xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1)
 
#按照题目的方法来
#/* Galois Field (256) Multiplication of two Bytes */
def GMul(u,v):
    tmpFlag = False
    if u == 0x3 and v == 0x72: tmpFlag=False
    p = 0
    tmpV = 0
    for i in range(8):
        if u & 0x01:
            p ^=v
        if tmpFlag: print('v:{:02X}-->'.format(v))
        tmpV = v
        v <<= 1#逻辑左移,要考虑超标问题
        if tmpFlag: print('v:{:02X}'.format(v))
        v = v & 0xFF#保证为字节
        if tmpFlag: print('v:{:02X}'.format(v))
        if tmpV & 0x80:#看看这个与,对什么造成了影响。这个是另外一个变量。判断的是循环右移之前的值
            v ^= 0x1B
            if tmpFlag: print('v:{:02X} #v & 0x80'.format(v))
        if tmpFlag: print('u:{:02X}<--'.format(u))
        u >>= 1#逻辑右移,要考虑高位是否存在问题
        if tmpFlag: print('u:{:02X}'.format(u))
        u = u & 0x7F  #去除高位           
    return p
 
#与题目保持一致
Rcon = (
    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
    0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,
    0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,
    0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39,
)
#原始数据,与CTF不一致
'''
static const uint32_t rcon[10] = {
        0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
        0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
'''
 
def PrintList(twoDlist):
    returnList = []
    for i in twoDlist:
        returnList.append(["{:02X}".format(j) for j in i])
    return returnList
 
def text2matrix(text):
    matrix = []
    #按行存放:   
    for i in range(16):
        byte = (text >> (8 * (15 - i))) & 0xFF
        if i % 4 == 0:
            matrix.append([byte])
        else:
            #matrix[i / 4].append(byte) #TypeError: list indices must be integers or slices, not float
            tmpI = i // 4
            matrix[tmpI].append(byte)
            #逆序存放
            #补充内容
            #print(matrix)
            if len(matrix[tmpI]) == 4:
                matrix[tmpI] = (matrix[tmpI])[::-1]
 
    '''
    #按列存放
    for i in range(16):
        byte = (text >> (8 * (15 - i))) & 0xFF
        if i < 4 :
            matrix.append([byte])
        else:
            #matrix[i / 4].append(byte) #TypeError: list indices must be integers or slices, not float
            matrix[i % 4].append(byte)
    '''
    return matrix
 
 
def matrix2text(matrix):
    text = 0
    for i in range(4):
        for j in range(4):
            text |= (matrix[i][j] << (120 - 8 * (4 * j + i)))
    return text
 
def plaintext2matrix(plaintext):
    matrix = []
    #按列存放
    for i in range(16):
        byte = (plaintext >> (8 * (15 - i))) & 0xFF
        if i < 4 :
            matrix.append([byte])           
        else:
            #matrix[i / 4].append(byte) #TypeError: list indices must be integers or slices, not float
            matrix[i % 4].append(byte) # 按列存放
        #print("i={:02X}\tbyte={:02X}".format(i,byte))
        #os.system("pause")
    return  matrix
 
def matrix2plaintext(matrix):
    text = ""
    #按列读取
    matrixTrans = list(map(list,zip(*matrix)))
    for i in matrixTrans:
        text += "".join(["{:02X}".format(j) for j in i])
    return text
 
class AES:
    def __init__(self, master_key):
        self.change_key(master_key)
 
    def change_key(self, master_key):
        self.round_keys = text2matrix(master_key)
        print("self.round_keys")
        print(PrintList(self.round_keys))
        print("#"*50)
 
        for i in range(4, 4 * 11):
            self.round_keys.append([])
            '''
            if i % 4 == 0:
                byte = self.round_keys[i - 4][0]        \
                     ^ Sbox[self.round_keys[i - 1][1]]  \#错误,此处应该是[i-1][3],它与CTF题目不一样
                     ^ Rcon[i // 4]
                     #^ Rcon[i / 4]
                self.round_keys[i].append(byte)
 
                for j in range(1, 4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ Sbox[self.round_keys[i - 1][(j + 1) % 4]]##错误,此处应该是[i-1][j-1],它与CTF题目不一样
                    self.round_keys[i].append(byte)
            else:
                for j in range(4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ self.round_keys[i - 1][j]
                    self.round_keys[i].append(byte)
            '''
            if i % 4 == 0:
                byte = self.round_keys[i - 4][0]        \
                     ^ Sbox[self.round_keys[i - 1][3]]  \
 
                     #^ Rcon[i // 4] #错误,应该挪一个位置 3
                     #^ Rcon[i / 4]
                self.round_keys[i].append(byte)
                '''
                print("i:{:02X}".format(i))
                print("byte:{:02X}".format(byte))
                print("self.round_keys[i - 4][0]:{:02X}".format(self.round_keys[i - 4][0]))
                print("self.round_keys[i - 1][3]:{:02X}".format(self.round_keys[i - 1][3]))
                print("Sbox[self.round_keys[i - 1][3]]:{:02X}".format(Sbox[self.round_keys[i - 1][3]]))
                print("Rcon[i // 4]:{:02X}".format(Rcon[i // 4]))
                os.system("pause")
                '''
                for j in range(1, 4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ Sbox[self.round_keys[i - 1][(j - 1) % 4]]
                    if j == 3:
                        byte ^=Rcon[i // 4]
                    self.round_keys[i].append(byte)
            else:
                for j in range(4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ self.round_keys[i - 1][j]
                    self.round_keys[i].append(byte)
        print("self.round_keys")
        print(PrintList(self.round_keys))
        #2022-05-21 2231 密钥扩展已处理完成,和题目一致
        print("#"*50)
 
    #修改:调整输入参数为是字节序列,而非一个超大整数
    def encrypt(self, plaintext):
        #按照0x10进行截断,填充模式采用 00 填充
        encryptText = ""
        last = len(plaintext) % 0x10
        cnt = len(plaintext) // 0x10
        for i in range(cnt) :
 
            byteTtext_0x10 = plaintext[i*0x10:(i+1)*0x10]
            plaintext_0x10 = int.from_bytes(byteTtext_0x10, byteorder='big')
            encryptText+=self.encrypt_0x10(plaintext_0x10)
 
        #处理余数。
        if last!= 0:
            byteTtext_0x10 = plaintext[cnt*0x10:]
            plaintext_0x10 = int.from_bytes(byteTtext_0x10, byteorder='big')
            plaintext_0x10 << (last*8)
            encryptText+=self.encrypt_0x10(plaintext_0x10)
 
        return encryptText
 
    #维持原有代码的效果,输入是一个 0x10字节长的整数
    def encrypt_0x10(self, plaintext):
        '''
        输入的字符串:'flag{db5c6a8dfec4d0ec5569899640}',0
        66 6C 61 67 7B 64 62 35  63 36 61 38 64 66 65 63 34 64 30 65 63 35 35 36 39 38 39 39 36 34 30 7D 00
        第一次为:Stack[00004680]:00EFE908 66 7B 63 64 6C 64 36 66+aFCdld6fabaeg58 db 'f{cdld6fabaeg58c'
        'f{cdld6fabaeg58c'
        '''
        tmpFlag = False
        #更换text转matrix方法
        #self.plain_state = text2matrix(plaintext)
        #换一个新方法
        if tmpFlag: print('plaintext:{:0X}'.format(plaintext))
        self.plain_state = plaintext2matrix(plaintext)
        if tmpFlag: print("*"*50+"\n"+"*"*50)
        if tmpFlag: print("第一轮:轮密钥加")
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        if tmpFlag: print("self.round_keys:{}".format(PrintList(self.round_keys[:4])))
        self.__add_round_key(self.plain_state, self.round_keys[:4])
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        #os.system("pause")#2022-05-21 2313 已与题目相符
 
        for i in range(1, 10):
            if tmpFlag:print("@"*70+"\n"+"第{}轮:".format(i))
            self.__round_encrypt(self.plain_state, self.round_keys[4 * i : 4 * (i + 1)])
 
        self.__sub_bytes(self.plain_state)
        self.__shift_rows(self.plain_state)
        self.__add_round_key(self.plain_state, self.round_keys[40:])
 
        if tmpFlag: print("*"*55 +"\n"+"跟踪:"+"matrix2text(self.plain_state)")#结果正确 
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        if tmpFlag: print("matrix2plaintext(self.plain_state):{}".format(matrix2plaintext(self.plain_state)))
        return matrix2plaintext(self.plain_state)
 
    #输入的是字节序列,而非一个超大整数
    def decrypt(self, ciphertext):
        decryptText=""
        #先判断长度是否满足要求,如果不是 0x10 整数倍,则退出
        lenCiphertext = len(ciphertext)
        if lenCiphertext % 0x10 != 0:
            return "输入错误,必须为0x10整数倍"
 
        cnt = lenCiphertext // 0x10
        print("lenCiphertext:{:02X}".format(lenCiphertext))
        print("cnt:{}".format(cnt))
        for i in range(cnt) :
            print("i:{}".format(i))
            byteCiphertext_0x10 = ciphertext[i*0x10:(i+1)*0x10]
            ciphertext_0x10 = int.from_bytes(byteCiphertext_0x10, byteorder='big')
            decryptText+=self.decrypt_x10(ciphertext_0x10)
            print("decryptText:{}".format(decryptText))
 
        return decryptText
 
    #与源代码保持一致,输入是整数
    def decrypt_x10(self, ciphertext):
        #self.cipher_state = text2matrix(ciphertext)
        tmpFlag = False
        if tmpFlag: print('ciphertext:{:0X}'.format(ciphertext))
        self.cipher_state = plaintext2matrix(ciphertext)
        if tmpFlag: print('ciphertext:{:0X}'.format(ciphertext))
 
        if tmpFlag: print("*"*50+"\n"+"*"*50)
 
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__add_round_key(self.cipher_state, self.round_keys[40:])")
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
        if tmpFlag: print("self.round_keys:{}".format(PrintList(self.round_keys[40:])))       
        self.__add_round_key(self.cipher_state, self.round_keys[40:])
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__inv_shift_rows(self.cipher_state)")
        self.__inv_shift_rows(self.cipher_state)
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__inv_sub_bytes(self.cipher_state)")
        self.__inv_sub_bytes(self.cipher_state)
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        for i in range(9, 0, -1):
            if tmpFlag:print("@"*70+"\n"+"第{}轮:".format(i))
            self.__round_decrypt(self.cipher_state, self.round_keys[4 * i : 4 * (i + 1)])
 
        if tmpFlag: print("最后一轮:轮密钥加")
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
        if tmpFlag: print("self.round_keys:{}".format(PrintList(self.round_keys[:4])))
        self.__add_round_key(self.cipher_state, self.round_keys[:4])
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        if tmpFlag: print("*"*55 +"\n"+"跟踪:"+"matrix2text(self.cipher_state)")#结果正确 
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
        if tmpFlag: print("matrix2plaintext(self.cipher_state):{}".format(matrix2plaintext(self.cipher_state)))
        #return matrix2text(self.cipher_state)
        return matrix2plaintext(self.cipher_state)
 
    #源代码,其与与题目不符合
    '''
    def __add_round_key(self, s, k):
        for i in range(4):
            for j in range(4):
                s[i][j] ^= k[i][j]
    '''
    #与题目保持一致,修改方法
    def __add_round_key(self, s, k):
        for i in range(4):
            for j in range(4):
                s[i][j] ^= k[j][3-i]
 
    def __round_encrypt(self, state_matrix, key_matrix):
        tmpFlag = False
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__sub_bytes(state_matrix)")#结果正确      
        self.__sub_bytes(state_matrix)
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
 
        if tmpFlag: print("*"*55 +"\n"+"跟踪:"+"self.__shift_rows(state_matrix)")#结果正确 
        self.__shift_rows(state_matrix)
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))#结果正确,排查完毕
 
        if tmpFlag: print("*"*55 +"\n""跟踪:"+"self.__mix_columns(state_matrix)")
        self.__mix_columns(state_matrix)
        #DB FE 10 D5 E9 25 E7 2F 34 D9 08 5C F2 9F 0A CD
        #错误
        #DB FE 10 CE E9 25 E7 2F 34 C2 08 5C F2 84 0A D6
        #         03             08 09          0D    0F          
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
 
        if tmpFlag: print("*"*55 +"\n""跟踪:"+"self.__add_round_key(state_matrix)")
        self.__add_round_key(state_matrix, key_matrix)
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        #os.system("pause")
 
 
    def __round_decrypt(self, state_matrix, key_matrix):
        self.__add_round_key(state_matrix, key_matrix)
        self.__inv_mix_columns(state_matrix)
        self.__inv_shift_rows(state_matrix)
        self.__inv_sub_bytes(state_matrix)
 
    def __sub_bytes(self, s):
        for i in range(4):
            for j in range(4):
                s[i][j] = Sbox[s[i][j]]
 
 
    def __inv_sub_bytes(self, s):
        for i in range(4):
            for j in range(4):
                s[i][j] = InvSbox[s[i][j]]
 
    #题目对循环操作相关的方向进行调整,其实就是正向逆向操作互换
    def __shift_rows(self, s):
        #原始AES:循环左移;题目是循环右移
        if not flag:
            s[1][0], s[1][1], s[1][2], s[1][3] = s[1][1], s[1][2], s[1][3], s[1][0]
            s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
            s[3][0], s[3][1], s[3][2], s[3][3] = s[3][3], s[3][0], s[3][1], s[3][2]
        else:
            s[1][0], s[1][1], s[1][2], s[1][3] = s[1][3], s[1][0], s[1][1], s[1][2]
            s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
            s[3][0], s[3][1], s[3][2], s[3][3] = s[3][1], s[3][2], s[3][3], s[3][0]
 
    def __inv_shift_rows(self, s):
        #原始AES:循环左移;题目是循环右移
        if not flag:
            s[1][0], s[1][1], s[1][2], s[1][3] = s[1][3], s[1][0], s[1][1], s[1][2]
            s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
            s[3][0], s[3][1], s[3][2], s[3][3] = s[3][1], s[3][2], s[3][3], s[3][0]
        else:
            s[1][0], s[1][1], s[1][2], s[1][3] = s[1][1], s[1][2], s[1][3], s[1][0]
            s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
            s[3][0], s[3][1], s[3][2], s[3][3] = s[3][3], s[3][0], s[3][1], s[3][2]
 
    #源代码,其与题目不符合
    '''
    def __mix_single_column(self, a):
        # please see Sec 4.1.2 in The Design of Rijndael
        t = a[0] ^ a[1] ^ a[2] ^ a[3]
        u = a[0]
        a[0] ^= t ^ xtime(a[0] ^ a[1])
        a[1] ^= t ^ xtime(a[1] ^ a[2])
        a[2] ^= t ^ xtime(a[2] ^ a[3])
        a[3] ^= t ^ xtime(a[3] ^ u)
 
    def __mix_columns(self, s):
        for i in range(4):
            self.__mix_single_column(s[i])
    '''
    #调整新的列混淆方法,与题目保持一致
    def __mix_columns(self, a):
        tmp=[]
        M= [[0x02, 0x03, 0x01, 0x01],[0x01, 0x02, 0x03, 0x01],[0x01, 0x01, 0x02, 0x03],[0x03, 0x01, 0x01, 0x02]]
        #调试:跟踪第轮:
        tmpMixFlag = False
        '''
        if a[0][1]==0x35 and a[0][1]==0x5E and a[0][1]==0xFD and a[0][1]==0x2E and a[0][1]==0x03 and a[0][1]==0xD0 and a[0][1]==0xCF and a[0][1]==0xD2 : tmpMixFlag = True
        '''
 
        #Copy
        for i in range(4):
            tmpInter=[]
            for j in range(4):
                tmpInter.append(a[i][j])
            tmp.append(tmpInter)
        #混淆
        for i in range(4):
            for j in range(4):
                a[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])
                if tmpMixFlag:
                    print("--"*20)
                    print("a[i][j]=a[{}][{}]={:02X}".format(i,j,a[i][j]))
                    print("GMul(M[{}][0], tmp[0][{}])=GMul({},{:02X})={:02X},累计:{:02X}".format(i,j,M[i][0], tmp[0][j],GMul(M[i][0], tmp[0][j]),GMul(M[i][0], tmp[0][j])))
                    print("GMul(M[{}][1], tmp[1][{}])=GMul({},{:02X})={:02X},累计:{:02X}".format(i,j,M[i][1], tmp[1][j],GMul(M[i][1], tmp[1][j]),GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])))
                    print("GMul(M[{}][2], tmp[2][{}])=GMul({},{:02X})={:02X},累计:{:02X}".format(i,j,M[i][2], tmp[2][j],GMul(M[i][2], tmp[2][j]),GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])^ GMul(M[i][2], tmp[2][j])))
                    print("GMul(M[{}][3], tmp[3][{}])=GMul({},{:02X})={:02X},累计:{:02X}".format(i,j,M[i][3], tmp[3][j],GMul(M[i][3], tmp[3][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])))
 
                    os.system("pause")
 
    #源代码,其与题目不符合
    '''
    def __inv_mix_columns(self, s):
        # see Sec 4.1.3 in The Design of Rijndael
        for i in range(4):
            u = xtime(xtime(s[i][0] ^ s[i][2]))
            v = xtime(xtime(s[i][1] ^ s[i][3]))
            s[i][0] ^= u
            s[i][1] ^= v
            s[i][2] ^= u
            s[i][3] ^= v
 
        self.__mix_columns(s)
    '''
 
    #调整新的列混淆方法,与题目保持一致
    #为什么会有多种AES的处理方式,那输入来说,字节逆序、比特逆序、行存放或列存放、列混淆方法有多种?
    def __inv_mix_columns(self, a):
        tmp=[]
        M= [[0x0E, 0x0B, 0x0D, 0x09],[0x09, 0x0E, 0x0B, 0x0D],[0x0D, 0x09, 0x0E, 0x0B],[0x0B, 0x0D, 0x09, 0x0E]]
        #Copy
        for i in range(4):
            tmpInter=[]
            for j in range(4):
                tmpInter.append(a[i][j])
            tmp.append(tmpInter)
        #混淆
        for i in range(4):
            for j in range(4):
                a[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])
 
##################################################################
##测试题目的密钥
print("#"*60+'\n'+"#"*60+'\n'+"测试解密")
##按字节顺序存储的。
##假定MD5的key已经获取到。可从内存中直接获取,也可以按附件md5-2.py来获取
key = '2F65B1FF31ED86D09A285C0F4048059D'
 
bKey = bytes.fromhex(key)
intKey = int.from_bytes(bKey, byteorder='big')
print("intKey:\t\t{:0X}".format(intKey))
testAES = AES(intKey)
 
SN = "flag{db5c6a8dfec4d0ec5569899640}"
#SN = "flag{db5c6a8dfec"
#SN = "4d0ec5569899640}"
bSN = bytes(SN,"utf-8")
 
#####修改传入的参数是 字节bytes类型
encrypted = testAES.encrypt(bSN)
print("encrypted:\t{}\n".format(encrypted))
 
print("#"*60+'\n'+"#"*60+"测试解密")
#解密方法还需要修正。
ciphertext = encrypted
ciphertextByte = bytes.fromhex(ciphertext)
print("ciphertextByte:\t{}\n".format(ciphertextByte))
#####修改传入的参数是 字节bytes类型
decrypted  = testAES.decrypt(ciphertextByte)
print("decrypted:{}".format(decrypted))
strBytes = bytes.fromhex(decrypted)
 
#以字符串形式输出
strCipher = strBytes.decode("utf-8")
print("strCipher:{}".format(strCipher))
#来源:https://github.com/heartingrass/AES-Python/blob/master/aes.py
#!/usr/bin/env python
#根据题目的加密方式做了修改
import os
"""
    Copyright (C) 2012 Bo Zhu http://about.bozhu.me
    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
"""
 
#定义:是否执行题目修改了原AES的算法
flag = True
#flag = False
 
Sbox = [
    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,
]
 
InvSbox = [
    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,
]
 
#题目更改了S盒子,那么就更改对应的逆盒子
#Python错误集锦:TypeError: ‘tuple’ object does not support item assignment
#解决方法:
#1、tuple不能修改元素,无解。如果要修改元素,改用list类型。
if flag :
    valueA = Sbox[0x71]
    valueB = Sbox[0xA3]
    #更改盒子
    Sbox[0x71] ^= Sbox[0xA3]
    Sbox[0xA3] ^= Sbox[0x71]
    Sbox[0x71] ^= Sbox[0xA3]
    #更改逆盒子
    InvSbox[valueA] ^= InvSbox[valueB]
    InvSbox[valueB] ^= InvSbox[valueA]
    InvSbox[valueA] ^= InvSbox[valueB]
    print("Sbox[0x71]:{:02X}".format(Sbox[0x71]))
    print("Sbox[0xA3]:{:02X}".format(Sbox[0xA3]))#结果错了。。。
 
 
# learnt from http://cs.ucsb.edu/~koc/cs178/projects/JT/aes.c
xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1)
 
#按照题目的方法来
#/* Galois Field (256) Multiplication of two Bytes */
def GMul(u,v):
    tmpFlag = False
    if u == 0x3 and v == 0x72: tmpFlag=False
    p = 0
    tmpV = 0
    for i in range(8):
        if u & 0x01:
            p ^=v
        if tmpFlag: print('v:{:02X}-->'.format(v))
        tmpV = v
        v <<= 1#逻辑左移,要考虑超标问题
        if tmpFlag: print('v:{:02X}'.format(v))
        v = v & 0xFF#保证为字节
        if tmpFlag: print('v:{:02X}'.format(v))
        if tmpV & 0x80:#看看这个与,对什么造成了影响。这个是另外一个变量。判断的是循环右移之前的值
            v ^= 0x1B
            if tmpFlag: print('v:{:02X} #v & 0x80'.format(v))
        if tmpFlag: print('u:{:02X}<--'.format(u))
        u >>= 1#逻辑右移,要考虑高位是否存在问题
        if tmpFlag: print('u:{:02X}'.format(u))
        u = u & 0x7F  #去除高位           
    return p
 
#与题目保持一致
Rcon = (
    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
    0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,
    0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,
    0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39,
)
#原始数据,与CTF不一致
'''
static const uint32_t rcon[10] = {
        0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
        0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
'''
 
def PrintList(twoDlist):
    returnList = []
    for i in twoDlist:
        returnList.append(["{:02X}".format(j) for j in i])
    return returnList
 
def text2matrix(text):
    matrix = []
    #按行存放:   
    for i in range(16):
        byte = (text >> (8 * (15 - i))) & 0xFF
        if i % 4 == 0:
            matrix.append([byte])
        else:
            #matrix[i / 4].append(byte) #TypeError: list indices must be integers or slices, not float
            tmpI = i // 4
            matrix[tmpI].append(byte)
            #逆序存放
            #补充内容
            #print(matrix)
            if len(matrix[tmpI]) == 4:
                matrix[tmpI] = (matrix[tmpI])[::-1]
 
    '''
    #按列存放
    for i in range(16):
        byte = (text >> (8 * (15 - i))) & 0xFF
        if i < 4 :
            matrix.append([byte])
        else:
            #matrix[i / 4].append(byte) #TypeError: list indices must be integers or slices, not float
            matrix[i % 4].append(byte)
    '''
    return matrix
 
 
def matrix2text(matrix):
    text = 0
    for i in range(4):
        for j in range(4):
            text |= (matrix[i][j] << (120 - 8 * (4 * j + i)))
    return text
 
def plaintext2matrix(plaintext):
    matrix = []
    #按列存放
    for i in range(16):
        byte = (plaintext >> (8 * (15 - i))) & 0xFF
        if i < 4 :
            matrix.append([byte])           
        else:
            #matrix[i / 4].append(byte) #TypeError: list indices must be integers or slices, not float
            matrix[i % 4].append(byte) # 按列存放
        #print("i={:02X}\tbyte={:02X}".format(i,byte))
        #os.system("pause")
    return  matrix
 
def matrix2plaintext(matrix):
    text = ""
    #按列读取
    matrixTrans = list(map(list,zip(*matrix)))
    for i in matrixTrans:
        text += "".join(["{:02X}".format(j) for j in i])
    return text
 
class AES:
    def __init__(self, master_key):
        self.change_key(master_key)
 
    def change_key(self, master_key):
        self.round_keys = text2matrix(master_key)
        print("self.round_keys")
        print(PrintList(self.round_keys))
        print("#"*50)
 
        for i in range(4, 4 * 11):
            self.round_keys.append([])
            '''
            if i % 4 == 0:
                byte = self.round_keys[i - 4][0]        \
                     ^ Sbox[self.round_keys[i - 1][1]]  \#错误,此处应该是[i-1][3],它与CTF题目不一样
                     ^ Rcon[i // 4]
                     #^ Rcon[i / 4]
                self.round_keys[i].append(byte)
 
                for j in range(1, 4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ Sbox[self.round_keys[i - 1][(j + 1) % 4]]##错误,此处应该是[i-1][j-1],它与CTF题目不一样
                    self.round_keys[i].append(byte)
            else:
                for j in range(4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ self.round_keys[i - 1][j]
                    self.round_keys[i].append(byte)
            '''
            if i % 4 == 0:
                byte = self.round_keys[i - 4][0]        \
                     ^ Sbox[self.round_keys[i - 1][3]]  \
 
                     #^ Rcon[i // 4] #错误,应该挪一个位置 3
                     #^ Rcon[i / 4]
                self.round_keys[i].append(byte)
                '''
                print("i:{:02X}".format(i))
                print("byte:{:02X}".format(byte))
                print("self.round_keys[i - 4][0]:{:02X}".format(self.round_keys[i - 4][0]))
                print("self.round_keys[i - 1][3]:{:02X}".format(self.round_keys[i - 1][3]))
                print("Sbox[self.round_keys[i - 1][3]]:{:02X}".format(Sbox[self.round_keys[i - 1][3]]))
                print("Rcon[i // 4]:{:02X}".format(Rcon[i // 4]))
                os.system("pause")
                '''
                for j in range(1, 4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ Sbox[self.round_keys[i - 1][(j - 1) % 4]]
                    if j == 3:
                        byte ^=Rcon[i // 4]
                    self.round_keys[i].append(byte)
            else:
                for j in range(4):
                    byte = self.round_keys[i - 4][j]    \
                         ^ self.round_keys[i - 1][j]
                    self.round_keys[i].append(byte)
        print("self.round_keys")
        print(PrintList(self.round_keys))
        #2022-05-21 2231 密钥扩展已处理完成,和题目一致
        print("#"*50)
 
    #修改:调整输入参数为是字节序列,而非一个超大整数
    def encrypt(self, plaintext):
        #按照0x10进行截断,填充模式采用 00 填充
        encryptText = ""
        last = len(plaintext) % 0x10
        cnt = len(plaintext) // 0x10
        for i in range(cnt) :
 
            byteTtext_0x10 = plaintext[i*0x10:(i+1)*0x10]
            plaintext_0x10 = int.from_bytes(byteTtext_0x10, byteorder='big')
            encryptText+=self.encrypt_0x10(plaintext_0x10)
 
        #处理余数。
        if last!= 0:
            byteTtext_0x10 = plaintext[cnt*0x10:]
            plaintext_0x10 = int.from_bytes(byteTtext_0x10, byteorder='big')
            plaintext_0x10 << (last*8)
            encryptText+=self.encrypt_0x10(plaintext_0x10)
 
        return encryptText
 
    #维持原有代码的效果,输入是一个 0x10字节长的整数
    def encrypt_0x10(self, plaintext):
        '''
        输入的字符串:'flag{db5c6a8dfec4d0ec5569899640}',0
        66 6C 61 67 7B 64 62 35  63 36 61 38 64 66 65 63 34 64 30 65 63 35 35 36 39 38 39 39 36 34 30 7D 00
        第一次为:Stack[00004680]:00EFE908 66 7B 63 64 6C 64 36 66+aFCdld6fabaeg58 db 'f{cdld6fabaeg58c'
        'f{cdld6fabaeg58c'
        '''
        tmpFlag = False
        #更换text转matrix方法
        #self.plain_state = text2matrix(plaintext)
        #换一个新方法
        if tmpFlag: print('plaintext:{:0X}'.format(plaintext))
        self.plain_state = plaintext2matrix(plaintext)
        if tmpFlag: print("*"*50+"\n"+"*"*50)
        if tmpFlag: print("第一轮:轮密钥加")
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        if tmpFlag: print("self.round_keys:{}".format(PrintList(self.round_keys[:4])))
        self.__add_round_key(self.plain_state, self.round_keys[:4])
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        #os.system("pause")#2022-05-21 2313 已与题目相符
 
        for i in range(1, 10):
            if tmpFlag:print("@"*70+"\n"+"第{}轮:".format(i))
            self.__round_encrypt(self.plain_state, self.round_keys[4 * i : 4 * (i + 1)])
 
        self.__sub_bytes(self.plain_state)
        self.__shift_rows(self.plain_state)
        self.__add_round_key(self.plain_state, self.round_keys[40:])
 
        if tmpFlag: print("*"*55 +"\n"+"跟踪:"+"matrix2text(self.plain_state)")#结果正确 
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        if tmpFlag: print("matrix2plaintext(self.plain_state):{}".format(matrix2plaintext(self.plain_state)))
        return matrix2plaintext(self.plain_state)
 
    #输入的是字节序列,而非一个超大整数
    def decrypt(self, ciphertext):
        decryptText=""
        #先判断长度是否满足要求,如果不是 0x10 整数倍,则退出
        lenCiphertext = len(ciphertext)
        if lenCiphertext % 0x10 != 0:
            return "输入错误,必须为0x10整数倍"
 
        cnt = lenCiphertext // 0x10
        print("lenCiphertext:{:02X}".format(lenCiphertext))
        print("cnt:{}".format(cnt))
        for i in range(cnt) :
            print("i:{}".format(i))
            byteCiphertext_0x10 = ciphertext[i*0x10:(i+1)*0x10]
            ciphertext_0x10 = int.from_bytes(byteCiphertext_0x10, byteorder='big')
            decryptText+=self.decrypt_x10(ciphertext_0x10)
            print("decryptText:{}".format(decryptText))
 
        return decryptText
 
    #与源代码保持一致,输入是整数
    def decrypt_x10(self, ciphertext):
        #self.cipher_state = text2matrix(ciphertext)
        tmpFlag = False
        if tmpFlag: print('ciphertext:{:0X}'.format(ciphertext))
        self.cipher_state = plaintext2matrix(ciphertext)
        if tmpFlag: print('ciphertext:{:0X}'.format(ciphertext))
 
        if tmpFlag: print("*"*50+"\n"+"*"*50)
 
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__add_round_key(self.cipher_state, self.round_keys[40:])")
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
        if tmpFlag: print("self.round_keys:{}".format(PrintList(self.round_keys[40:])))       
        self.__add_round_key(self.cipher_state, self.round_keys[40:])
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__inv_shift_rows(self.cipher_state)")
        self.__inv_shift_rows(self.cipher_state)
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__inv_sub_bytes(self.cipher_state)")
        self.__inv_sub_bytes(self.cipher_state)
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        for i in range(9, 0, -1):
            if tmpFlag:print("@"*70+"\n"+"第{}轮:".format(i))
            self.__round_decrypt(self.cipher_state, self.round_keys[4 * i : 4 * (i + 1)])
 
        if tmpFlag: print("最后一轮:轮密钥加")
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
        if tmpFlag: print("self.round_keys:{}".format(PrintList(self.round_keys[:4])))
        self.__add_round_key(self.cipher_state, self.round_keys[:4])
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
 
        if tmpFlag: print("*"*55 +"\n"+"跟踪:"+"matrix2text(self.cipher_state)")#结果正确 
        if tmpFlag: print("self.cipher_state:{}".format(PrintList(self.cipher_state)))
        if tmpFlag: print("matrix2plaintext(self.cipher_state):{}".format(matrix2plaintext(self.cipher_state)))
        #return matrix2text(self.cipher_state)
        return matrix2plaintext(self.cipher_state)
 
    #源代码,其与与题目不符合
    '''
    def __add_round_key(self, s, k):
        for i in range(4):
            for j in range(4):
                s[i][j] ^= k[i][j]
    '''
    #与题目保持一致,修改方法
    def __add_round_key(self, s, k):
        for i in range(4):
            for j in range(4):
                s[i][j] ^= k[j][3-i]
 
    def __round_encrypt(self, state_matrix, key_matrix):
        tmpFlag = False
        if tmpFlag: print("*"*55 +"\n"+ "跟踪:"+"self.__sub_bytes(state_matrix)")#结果正确      
        self.__sub_bytes(state_matrix)
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
 
        if tmpFlag: print("*"*55 +"\n"+"跟踪:"+"self.__shift_rows(state_matrix)")#结果正确 
        self.__shift_rows(state_matrix)
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))#结果正确,排查完毕
 
        if tmpFlag: print("*"*55 +"\n""跟踪:"+"self.__mix_columns(state_matrix)")
        self.__mix_columns(state_matrix)
        #DB FE 10 D5 E9 25 E7 2F 34 D9 08 5C F2 9F 0A CD
        #错误
        #DB FE 10 CE E9 25 E7 2F 34 C2 08 5C F2 84 0A D6
        #         03             08 09          0D    0F          
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
 
        if tmpFlag: print("*"*55 +"\n""跟踪:"+"self.__add_round_key(state_matrix)")
        self.__add_round_key(state_matrix, key_matrix)
        if tmpFlag: print("self.plain_state:{}".format(PrintList(self.plain_state)))
        #os.system("pause")
 
 
    def __round_decrypt(self, state_matrix, key_matrix):
        self.__add_round_key(state_matrix, key_matrix)
        self.__inv_mix_columns(state_matrix)
        self.__inv_shift_rows(state_matrix)
        self.__inv_sub_bytes(state_matrix)
 
    def __sub_bytes(self, s):
        for i in range(4):
            for j in range(4):
                s[i][j] = Sbox[s[i][j]]
 
 
    def __inv_sub_bytes(self, s):
        for i in range(4):
            for j in range(4):
                s[i][j] = InvSbox[s[i][j]]
 
    #题目对循环操作相关的方向进行调整,其实就是正向逆向操作互换
    def __shift_rows(self, s):
        #原始AES:循环左移;题目是循环右移
        if not flag:
            s[1][0], s[1][1], s[1][2], s[1][3] = s[1][1], s[1][2], s[1][3], s[1][0]
            s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
            s[3][0], s[3][1], s[3][2], s[3][3] = s[3][3], s[3][0], s[3][1], s[3][2]
        else:
            s[1][0], s[1][1], s[1][2], s[1][3] = s[1][3], s[1][0], s[1][1], s[1][2]
            s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-5-23 13:05 被htg编辑 ,原因: 完善算法内容
上传的附件:
收藏
免费 4
支持
分享
最新回复 (3)
雪    币: 5568
活跃值: (3218)
能力值: ( LV12,RANK:407 )
在线值:
发帖
回帖
粉丝
2
2022-5-25 21:48
0
雪    币: 50161
活跃值: (20625)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
感谢视频分享!
2022-5-25 22:08
0
雪    币: 4134
活跃值: (5847)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
4
htg https://www.bilibili.com/video/BV1Vv4y1P7St?spm_id_from=333.999.0.0
感谢分享,大佬讲解得很棒。
2022-6-3 14:16
0
游客
登录 | 注册 方可回帖
返回
//