-
-
[原创]看雪 2022·KCTF 春季赛 > 第三题 石像病毒 by 心学
-
2022-5-16 23:22 11544
-
日期:2022-05-14
CTF:htg
题目:看雪 2022·KCTF 春季赛 > 第三题 石像病毒
工具:IDA
要点:算法 MD5、AES
一、预习算法知识
1.1、MD5
1.1.1 定义:
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
将任意长度的明文,转化为128位的哈希值。
1.1.2 算法流程
1.1.2.1 明文处理
1.1.2.1.1 转储字节
明文字符串转储为字节序列,默认编码格式为utf-8
1.1.2.1.2 补充至448位
将字节序列长度整除512,如余数不等于448,则在后面补充1个1和多个0,直至整除512的余数为448
1.1.2.1.3 添加长度
接着在后面添加字节序列的长度(二进制位数)
1.1.2.1.4 补充至512位
接着在后面补0,直至长度被512整除
1.1.2.2 设置初值
设置后续循环计算的初始值
1 2 3 4 | A = 0x67452301 B = 0xEFCDAB89 C = 0x98BADCFE D = 0x10325476 |
1.1.2.3 循环运算
每次处理512个字节,直至处理完成,即循环次数为:预处理后的字节序列长度 / 512 的结果
基本概念
四种函数
1 2 3 4 | 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)) |
针对四种函数,定义与其对应的四个
1 2 3 4 5 6 7 8 9 | 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个子分组(从 0 到 15 ,共 16 个) 2 )<<< s表示循环左移s位 3 )常数tj为:在第j步中,tj是 4294967296 * abs (sin(j))的整数部分,i的单位是弧度。( 4294967296 是 2 的 32 次方)亦可用 0x100000000UL * abs (sin((double)j)) 计算 4 )i和j 都是针对一次主循环而言的,下一个主循环开始后,i和j将重置。 5 )a,b,c,d 就是每一步中的 A,B,C,D |
以一个512字节序列的过程如下:
对预处理后的字节序列进行分组
分组后的数组长度为 16 ,每个单元长度为32,即4个字节
分别进行16次FF、GG、HH、II运算,每次运行时,ABCD的参数进行右移替换
以16次FF举例,分别是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 ) |
1.1.2.4 反转合并
实际上是将int类型的值在内存中字节序列原位输出。
比如:0x01234567,其在内存中存储序列为 67452301,那么反转之后为'67452301',将四个字符串合并输出,即为 md5 hash
1.1.3 算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | #来源: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)) |
1.1.4 算法要点回顾
初始值是识别算法的特征
两处扩充(448、512)
有4种基本操作的函数
MD5_K_Order有0x64,存储sin值
主循环内有64次子循环
1.2、AES
算法详解:https://blog.csdn.net/u010037269/article/details/123863979
主要概念:
1.2.1 AES基本概念
AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。
密钥的长度可以使用128位(10轮)、192位(12轮)或256位(14轮)。密钥的长度不同,推荐加密轮数也不同。
本题是AES-128,有11轮操作
AES的处理单位是字节
图片引自https://blog.csdn.net/u010037269/article/details/123863979
1.2.2 AES操作
1.2.2.1、分组:
128位的输入明文分组P被分成16个字节
1.2.2.2、密钥扩充
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.2.2.3、加密过程
加密的第1轮到第9轮的轮函数一样,包括4个操作:字节代换、行位移、列混合和轮密钥加。最后一轮迭代不执行列混合。另外,在第一轮迭代之前,先将明文和原始密钥进行一次异或加密操作。
1.2.2.3.1、字节代换
AES的字节代换其实就是一个简单的查表操作。AES定义了一个S盒和一个逆S盒。
1.2.2.3.2、行位移
行移位是一个简单的左循环移位操作。当密钥长度为128比特时,状态矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节
1.2.2.3.3、列混合
列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵
1.2.2.3.4、轮密钥加
轮密钥加是将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作
1.2.2.4 分组方式:本题是ECB模式
分组密码有五种工作体制:1.电码本模式(Electronic Codebook Book (ECB));2.密码分组链接模式(Cipher Block Chaining (CBC));3.计算器模式(Counter (CTR));4.密码反馈模式(Cipher FeedBack (CFB));5.输出反馈模式(Output FeedBack (OFB))。
1.2.4 算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | #来源: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)) |
二、代码结构分析
2.0、观察程序运行
双击运行程序,出现一个cmd窗口,随便输入信息,程序自动关闭
打开cmd,将程序拖入进去,回车,随便输入信息,显示 NO
2.1、找主程序
用IDA打开,分析之后,找到main函数,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | int __cdecl main_0( int argc, const char * * argv, const char * * envp) { void * v3; / / esp char v7; / / [esp - 10h ] [ebp - 1048h ] int v8[ 1032 ]; / / [esp + 0h ] [ebp - 1038h ] BYREF CPPEH_RECORD ms_exc; / / [esp + 1020h ] [ebp - 18h ] v3 = alloca( 0x1020 ); memset(v8, 0xCCu , sizeof(v8)); ms_exc.registration.ScopeTable = (PSCOPETABLE_ENTRY)(( int )ms_exc.registration.ScopeTable ^ __security_cookie); memset(&v8[ 0x1E ], 0 , 0xFA0u ); memset(&v8[ 0x12 ], 0 , 0x28 ); gets_s((char * )&v8[ 30 ], 0xFA0u ); / / 输入序列号 if ( strlen((const char * )&v8[ 30 ]) = = 0x20 ) / / 限定长度: 0x20 { for ( v8[ 0x10 ] = 0 ; v8[ 0x10 ] < 0x200 ; + + v8[ 0x10 ] ) { v8[ 15 ] = 0 ; MEMORY[ 0 ] = v8[ 0x10 ]; / / 反调试:程序触发异常 ms_exc.registration.TryLevel = 0xFFFFFFFE ; } strcpy((char * )&v8[ 9 ], "Enj0y_1t_4_fuuuN" ); _DX = 0x5F00 ; * (_WORD * )((char * )&v8[ 13 ] + 1 ) = 0 ; HIBYTE(v8[ 0xD ]) = 0 ; memset(&v8[ 2 ], 0 , 0x14 ); for ( v8[ 0 ] = 0 ; v8[ 0 ] < 0x200 ; + + v8[ 0 ] ) { __asm { insb; Input Byte(s) from Port to String } / / 反调试:程序触发异常 ms_exc.registration.TryLevel = - 2 ; _DX = LOWORD(v8[ 0 ]) + 1 ; } sub_61109(&v8[ 9 ], 0x10u , ( int )&v8[ 2 ]); / / MD5算法(已改) sub_61104(&v8[ 2 ], 0x10u , &v8[ 30 ], ( int )&v8[ 18 ], 0x20u ); / / AES算法(已改) if ( !memcmp(&v8[ 0x12 ], Buf2, 0x20u ) ) / / Buf2:全局变量,但有一处对其进行了修改 sub_61037( "OK\n" , v7); else sub_61037( "NO\n" , v7); return 0 ; } else { sub_61037( "NO\n" , v7); return 0 ; } } |
主程序架构不算复杂,主要结构如下:
2.1.1、获取用户输入,长度限定0x20字节
1 2 | gets_s((char * )&v8[ 30 ], 0xFA0u ); / / 输入序列号 if ( strlen((const char * )&v8[ 30 ]) = = 0x20 ) / / 限定长度: 0x20 |
2.1.2、触发反调试:程序主动抛出异常
1 2 3 4 5 6 | for ( v8[ 0x10 ] = 0 ; v8[ 0x10 ] < 0x200 ; + + v8[ 0x10 ] ) { v8[ 15 ] = 0 ; MEMORY[ 0 ] = v8[ 0x10 ]; / / 反调试:程序触发异常 ms_exc.registration.TryLevel = 0xFFFFFFFE ; } |
此时,要观察汇编代码,才能看出程序修改了哪些内容。
我们可以找到上述代码位置,异常处理有三块:
.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接管这个异常,让应用程序自身的机制去处理。
后续出现的反调试,都可以按照这个来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | .text: 000628D3 ; _ _try { / / __except at loc_62901 .text: 000628D3 104C mov [ebp + ms_exc.registration.TryLevel], 0 .text: 000628DA 104C mov [ebp + var_FFC], 0 .text: 000628E4 104C mov edx, [ebp + var_FFC] .text: 000628EA 104C mov al, byte ptr [ebp + var_FF8] .text: 000628F0 104C mov [edx], al ; 抛出异常 .text: 000628F0 ; } / / starts at 628D3 .text: 000628F2 104C mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 000628F9 104C jmp short loc_6290B .text: 000628F9 .text: 00062901 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00062901 .text: 00062901 loc_62901: .text: 00062901 ; _ _except(loc_628FB) / / owned by 628D3 .text: 00062901 104C mov esp, [ebp + ms_exc.old_esp] .text: 00062904 104C mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00062904 .text: 000628FB ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 000628FB .text: 000628FB loc_628FB: .text: 000628FB ; _ _except filter / / owned by 628D3 .text: 000628FB 104C mov eax, 1 .text: 00062900 104C retn .text: 00062900 |
2.1.3、内置字符串A赋值给某个堆的位置
strcpy((char *)&v8[9], "Enj0y_1t_4_fuuuN");
2.1.4、初始化某个变量(其实是MD5(A))
1 | memset(&v8[ 2 ], 0 , 0x14 ); |
2.1.5、触发反调试:程序主动抛出异常:同上面的处理
1 2 3 4 5 6 | for ( v8[ 0 ] = 0 ; v8[ 0 ] < 0x200 ; + + v8[ 0 ] ) { __asm { insb; Input Byte(s) from Port to String } / / 反调试:程序触发异常 ms_exc.registration.TryLevel = - 2 ; _DX = LOWORD(v8[ 0 ]) + 1 ; } |
2.1.6、将内置字符生成一个0x10长度的字节序列,其实是伪MD5(作者修改了其内容)
1 | sub_61109(&v8[ 9 ], 0x10u , ( int )&v8[ 2 ]); / / MD5算法(已改) |
2.1.7、使用刚才的0x10字节的序列以及用户序列号,生成一个0x20字节的序列,实际是伪AES算法(已改)
1 | sub_61104(&v8[ 2 ], 0x10u , &v8[ 30 ], ( int )&v8[ 18 ], 0x20u ); / / AES算法(已改) |
2.1.8、其结果与内置的Buf2进行对比,相等则成功
1 | if ( !memcmp(&v8[ 0x12 ], Buf2, 0x20u ) ) / / Buf2:全局变量,但有一处对其进行了修改 |
自此,主程序代码分析完成,基本框架清楚,主要集中在两个函数sub_61109和sub_61104
2.2、分析sub_61109
主程序代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | void * __cdecl sub_62DB0(void * Src, size_t Size, _DWORD * a3) { void * result; / / eax int v4; / / [esp + 14h ] [ebp - 9Ch ] unsigned int v5; / / [esp + 18h ] [ebp - 98h ] int v6; / / [esp + 1Ch ] [ebp - 94h ] unsigned int k; / / [esp + 20h ] [ebp - 90h ] unsigned int m; / / [esp + 20h ] [ebp - 90h ] int v9; / / [esp + 24h ] [ebp - 8Ch ] int v10; / / [esp + 28h ] [ebp - 88h ] int v11; / / [esp + 2Ch ] [ebp - 84h ] int v12; / / [esp + 30h ] [ebp - 80h ] int v13[ 17 ]; / / [esp + 38h ] [ebp - 78h ] unsigned int j; / / [esp + 7Ch ] [ebp - 34h ] unsigned int i; / / [esp + 80h ] [ebp - 30h ] void * Block; / / [esp + 84h ] [ebp - 2Ch ] int v17; / / [esp + 88h ] [ebp - 28h ] int v18; / / [esp + 8Ch ] [ebp - 24h ] int v19; / / [esp + 90h ] [ebp - 20h ] int v20; / / [esp + 94h ] [ebp - 1Ch ] CPPEH_RECORD ms_exc; / / [esp + 98h ] [ebp - 18h ] Block = 0 ; MEMORY[ 0 ] = 1 ; / / 有反调试:出发主动异常 ms_exc.registration.TryLevel = - 2 ; v20 = 0x67452301 ; v19 = 0xEFCDAB89 ; v18 = 0x98BADCFE ; v17 = 0x10325476 ; for ( i = Size + 1 ; i % 0x40 ! = 0x38 ; + + i ) ; result = malloc(__CFADD__(i, 8 ) ? - 1 : i + 8 ); / / 分配 0x40 空间 Block = result; if ( result ) { memcpy(Block, Src, Size); * ((_BYTE * )Block + Size) = 0x80 ; / / MD5算法的前期处理:补充位数至 448 位。 / / 1 )填充:首先将输入信息的长度(bit)进行填充,使得对 512 求余的结果等于 448 。填充的方法是填充一个 1 和n个 0 。 for ( j = Size + 1 ; j < i; + + j ) * ((_BYTE * )Block + j) = 0 ; sub_610AF( 8 * Size, (char * )Block + i); / / 2 )记录信息长度:用 64 位来存储填充前信息长度。这 64 位加在第一步结果的后面,这样信息长度就变为N * 512 + 448 + 64 = (N + 1 ) * 512 位。 / / 存储信息长度:按bit计算 sub_610AF(Size >> 0x1D , (char * )Block + i + 4 ); / / 如果Size长度过大,将高字节部分存入。最后的 8 字节就是明文信息长度 for ( j = 0 ; j < i; j + = 0x40 ) { for ( k = 0 ; k < 0x10 ; + + k ) v13[k] = sub_611CC((unsigned __int16 * )((char * )Block + 4 * k + j)); / / 存在反调试:主动触发异常,并修改了MD5内的一个DWORD v12 = v20; / / A v11 = v19; / / B v10 = v18; / / C v9 = v17; / / D for ( m = 0 ; m < 0x40 ; + + m ) { if ( m > = 0x10 ) { if ( m > = 0x20 ) { if ( m > = 0x30 ) { v6 = v10 ^ (v11 | ~v9); v5 = 7 * m % 0x10 ; } else { v6 = v9 ^ v10 ^ v11; v5 = ( 3 * m + 5 ) % 0x10 ; } } else { v6 = v10 & ~v9 | v11 & v9; v5 = ( 5 * m + 1 ) % 0x10 ; } } else { v6 = v9 & ~v11 | v10 & v11; v5 = m; } v4 = v9; / / D v9 = v10; / / D = C v10 = v11; / / C = B v11 + = __ROL4__(v13[v5] + MD5_K_order[m] + v6 + v12, dword_68B88[m]); / / B = temp v12 = v4; / / A = D } v20 + = v12; / / 更新 A、B、C、D的值,作为下一个 512 位数据处理的初始值。 v19 + = v11; v18 + = v10; v17 + = v9; } free(Block); sub_610AF(v20, a3); sub_610AF(v19, a3 + 1 ); sub_610AF(v18, a3 + 2 ); return (void * )sub_610AF(v17, a3 + 3 ); } return result; } |
2.2.1、关键位置
1 | v13[k] = sub_611CC((unsigned __int16 * )((char * )Block + 4 * k + j)); / / 存在反调试:主动触发异常,并修改了MD5内的一个DWORD |
在这里有一处主动触发异常,修改MD5算法,直接查看伪代码是看不来来的
1 2 3 4 5 6 7 8 9 | / / attributes: thunk int __cdecl sub_611CC(unsigned __int16 * a1) { return sub_62CE0(a1); } int __cdecl sub_62CE0(unsigned __int16 * a1) { return ( * ((unsigned __int8 * )a1 + 3 ) << 24 ) | ( * ((unsigned __int8 * )a1 + 2 ) << 16 ) | * a1; / / 存在反调试:主动异常,同时修改MD5 } |
而查看 sub_62CE0 的汇编代码 graphic结构,会发现右侧有一个异常filter代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | .text: 00062D13 ; __try { / / __except at loc_62D34 .text: 00062D13 02C mov [ebp + ms_exc.registration.TryLevel], 0 .text: 00062D1A 02C xor ebx, ebx .text: 00062D1C 02C div ebx ; 反调试 .text: 00062D1C ; } / / starts at 62D13 .text: 00062D1E 02C mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00062D25 02C jmp short loc_62D3E .text: 00062D25 .text: 00062D34 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00062D34 .text: 00062D34 loc_62D34: .text: 00062D34 ; __except(loc_62D27) / / owned by 62D13 .text: 00062D34 02C mov esp, [ebp + ms_exc.old_esp] .text: 00062D37 02C mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00062D37 .text: 00062D27 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00062D27 .text: 00062D27 loc_62D27: .text: 00062D27 ; __except filter / / owned by 62D13 .text: 00062D27 02C mov eax, [ebp + ms_exc.exc_ptr] .text: 00062D2A 02C push eax .text: 00062D2B 030 call sub_610FA .text: 00062D2B .text: 00062D30 030 add esp, 4 .text: 00062D33 02C retn .text: 00062D33 |
在 __except filter // owned by 62D13 内有一个方法call sub_610FA,他会对 对 MD5_K_order[ecx], 0D4AF3085h 进行修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | .text: 000610FA .text: 000610FA .text: 000610FA ; Attributes: thunk .text: 000610FA .text: 000610FA sub_610FA proc near .text: 000610FA 000 jmp sub_62C60 .text: 000610FA .text: 000610FA sub_610FA endp .text: 000610FA .text: 00062C60 .text: 00062C60 .text: 00062C60 ; Attributes: bp - based frame .text: 00062C60 .text: 00062C60 ; int sub_62C60() .text: 00062C60 sub_62C60 proc near .text: 00062C60 000 push ebp .text: 00062C61 004 mov ebp, esp .text: 00062C63 004 mov eax, 4 .text: 00062C68 004 imul ecx, eax, 2Ah ; '*' .text: 00062C6B 004 mov MD5_K_order[ecx], 0D4AF3085h .text: 00062C75 004 mov eax, 1 .text: 00062C7A 004 pop ebp .text: 00062C7B 000 retn .text: 00062C7B .text: 00062C7B sub_62C60 endp .text: 00062C7B .text: 00062C7B ; - - - - - - |
这里面出现了 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
2.3、分析sub_61104
2.3.1、主程序代码
1 2 3 4 5 | / / attributes: thunk int __cdecl sub_61104(void * interTansInfo, size_t interLen, _BYTE * inputSN, int outputBuffer, unsigned int outputLen) { return sub_62070(interTansInfo, interLen, inputSN, outputBuffer, outputLen); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | int __cdecl sub_62070( void * interMD5_TansInfo, size_t interLen, _BYTE * inputSN, int outputBuffer, unsigned int outputLen) { int j; / / [esp + 4h ] [ebp - 1C8h ] unsigned int i; / / [esp + 8h ] [ebp - 1C4h ] int v8[ 6 ]; / / [esp + 10h ] [ebp - 1BCh ] BYREF int v9[ 11 ]; / / [esp + 28h ] [ebp - 1A4h ] BYREF char * v10; / / [esp + 54h ] [ebp - 178h ] int outputBuffer2; / / [esp + 58h ] [ebp - 174h ] char v12[ 360 ]; / / [esp + 60h ] [ebp - 16Ch ] BYREF outputBuffer2 = outputBuffer; v10 = v12; memset(&v9[ 6 ], 0 , 0x10 ); memset(v9, 0 , 0x10 ); memset(v8, 0 , 0x10 ); if ( !interMD5_TansInfo || !inputSN || !outputBuffer ) / / 都不能为空 return - 1 ; if ( interLen > 0x10 ) / / 必须小于等于 0x10 ,这是内置的,不太可能不成功 return - 1 ; if ( outputLen % 0x10 ) / / 如果输出的长度不满足要求,则退出,不过这里不可能,传入的就是 0x20 return - 1 ; memcpy(v9, interMD5_TansInfo, interLen); sub_610BE(( int )v9, 0x10 , ( int )v12); / / 对内部信息进行转换,初始化AES盒子,但是内部存在一个主动触发异常,修改了盒子内容 for ( i = 0 ; i < outputLen; i + = 0x10 ) / / 一次处理 0x10 ,处理 2 遍 { j_snTo2Darray(( int )v8, inputSN); sub_61186(( int )v8, ( int )v10); for ( j = 1 ; j < 0xA ; + + j ) { v10 + = 0x10 ; sub_61172(( int )v8); / / 字节代替 sub_6122B(( int )v8); / / 行位移 sub_6100F(( int )v8); / / 列混淆 sub_61186(( int )v8, ( int )v10); / / 轮密钥相加 } sub_61172(( int )v8); / / 字节代替 sub_6122B(( int )v8); / / 行位移 sub_61186(( int )v8, ( int )(v10 + 0x10 )); / / 轮密钥相加 sub_6125D(( int )v8, outputBuffer2); / / 将分组密文保存到输出缓冲区 outputBuffer2 + = 0x10 ; inputSN + = 0x10 ; v10 = v12; } return 0 ; } |
2.3.2、分析初始化AES盒子代码 sub_610BE
在IDA里,采用graphics模式,我们观察到 右侧有异常filter代码,证明里面存在try代码,猜测可能存在主动出发异常,在阅读代码的时候,发现有一段代码不连续
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | .text: 0006167A ; __try { / / __except at loc_61698 .text: 0006167A 044 mov [ebp + ms_exc.registration.TryLevel], 0 .text: 00061681 044 inc edi .text: 00061683 044 inc ebp .text: 00061684 044 cld .text: 00061684 ; } / / starts at 6167A .text: 00061684 ; } / / starts at 615E0 .text: 00061684 sub_615E0 endp .text: 00061684 .text: 00061684 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00061685 dd 0FFFFFFFEh .text: 00061689 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00061689 jmp short loc_616A2 .text: 0006168B ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 0006168B ; START OF FUNCTION CHUNK FOR sub_615E0 .text: 0006168B .text: 0006168B loc_6168B: ; DATA XREF: .rdata:stru_69FF0↓o .text: 0006168B ; __unwind { / / __except_handler4 .text: 0006168B ; __except filter / / owned by 6167A .text: 0006168B 000 mov eax, [ebp + ms_exc.exc_ptr] .text: 0006168E 000 push eax .text: 0006168F 004 call sub_6108C .text: 00061694 004 add esp, 4 .text: 00061697 000 retn .text: 00061698 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
.text:00061685 dd 0FFFFFFFEh
触发了 An attempt was made to execute an illegal instruction (exc.code c000001d,tid 11028)
进而会将代码跳转到 __except filter // owned by 6167A
1 2 3 4 5 6 7 8 9 10 11 12 13 | .text: 0006168B ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 0006168B ; START OF FUNCTION CHUNK FOR sub_615E0 .text: 0006168B .text: 0006168B loc_6168B: .text: 0006168B ; __unwind { / / __except_handler4 .text: 0006168B ; __except filter / / owned by 6167A .text: 0006168B 000 8B 45 EC mov eax, [ebp + ms_exc.exc_ptr] .text: 0006168E 000 50 push eax .text: 0006168F 004 E8 F8 F9 FF FF call sub_6108C .text: 0006168F .text: 00061694 004 83 C4 04 add esp, 4 .text: 00061697 000 C3 retn .text: 00061697 |
我们发现右侧还有两个,证明还存在2处触发异常的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | .text: 0006174D ; __try { / / __except at loc_6176B .text: 0006174D 044 C7 45 FC 01 00 00 00 mov [ebp + ms_exc.registration.TryLevel], 1 .text: 00061754 044 EF out dx, eax .text: 00061754 ; } / / starts at 6174D .text: 00061755 044 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 0006175C 044 EB 17 jmp short loc_61775 .text: 0006175C .text: 0006176B ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 0006176B .text: 0006176B ; int __usercall loc_6176B@<eax>( int @<ebp>) .text: 0006176B loc_6176B: .text: 0006176B ; __except(loc_6175E) / / owned by 6174D .text: 0006176B 044 8B 65 E8 mov esp, [ebp + ms_exc.old_esp] .text: 0006176E 044 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 0006176E .text: 0006175E ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 0006175E .text: 0006175E ; int loc_6175E() .text: 0006175E loc_6175E: .text: 0006175E ; __except filter / / owned by 6174D .text: 0006175E 000 8B 4D EC mov ecx, [ebp + ms_exc.exc_ptr] .text: 00061761 000 51 push ecx .text: 00061762 004 E8 25 F9 FF FF call sub_6108C .text: 00061762 .text: 00061767 004 83 C4 04 add esp, 4 .text: 0006176A 000 C3 retn .text: 0006176A |
这是最后一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | .text: 000618E8 ; __try { / / __except at loc_61906 .text: 000618E8 044 C7 45 FC 02 00 00 00 mov [ebp + ms_exc.registration.TryLevel], 2 .text: 000618EF 044 6C insb .text: 000618EF ; } / / starts at 618E8 .text: 000618F0 044 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 000618F7 044 EB 17 jmp short loc_61910 .text: 000618F7 .text: 00061906 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00061906 .text: 00061906 ; void __usercall loc_61906( int @<ebp>) .text: 00061906 loc_61906: .text: 00061906 ; __except(loc_618F9) / / owned by 618E8 .text: 00061906 044 8B 65 E8 mov esp, [ebp + ms_exc.old_esp] .text: 00061909 044 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00061909 .text: 000618F9 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 000618F9 .text: 000618F9 ; int loc_618F9() .text: 000618F9 loc_618F9: .text: 000618F9 ; __except filter / / owned by 618E8 .text: 000618F9 000 8B 55 EC mov edx, [ebp + ms_exc.exc_ptr] .text: 000618FC 000 52 push edx .text: 000618FD 004 E8 8A F7 FF FF call sub_6108C .text: 000618FD .text: 00061902 004 83 C4 04 add esp, 4 .text: 00061905 000 C3 retn .text: 00061905 |
以上三处触发异常的filter,都是执行了一个方法 call sub_6108C
这个方法具体做了啥?对AES合资进行了交换,71 与 A3 进行了互换
我们查AES原始的盒子【71】=A3;【A3】=0A
那么我们构造逆盒子的时候,就应该将 0A 和 A3 进行替换
1 2 3 4 5 6 7 8 9 10 11 12 | / / attributes: thunk int sub_6108C() { return sub_62330(); } int sub_62330() { Rijndael_sbox[ 0x71 ] ^ = Rijndael_sbox[ 0xA3 ]; Rijndael_sbox[ 0xA3 ] ^ = Rijndael_sbox[ 0x71 ]; Rijndael_sbox[ 0x71 ] ^ = Rijndael_sbox[ 0xA3 ]; return 1 ; } |
2.3.3、分析行位移 sub_6122B
1 2 3 4 5 | / / attributes: thunk int __cdecl sub_6122B( int a1) { return sub_61AE0(a1); } |
sub_61AE0 无法F5,直接在graphics里观看,发现一处异常处理代码
1 2 3 4 5 6 7 | .text: 00061C26 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00061C26 .text: 00061C26 loc_61C26: .text: 00061C26 ; __except filter / / owned by 61BE5 .text: 00061C26 000 B8 01 00 00 00 mov eax, 1 .text: 00061C2B 000 C3 retn .text: 00061C2B |
其关联如下:这是 try 代码,触发异常之后,这段代码会回滚
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | .text: 00061BE5 ; __try { / / __except at loc_61C2C .text: 00061BE5 04C C7 45 FC 00 00 00 00 mov [ebp + ms_exc.registration.TryLevel], 0 .text: 00061BEC 04C CD 8B int 8Bh ; used by BASIC while in interpreter .text: 00061BEC .text: 00061BEE 04C 4D dec ebp .text: 00061BEF 04C C8 C1 E1 03 enter 0FFFFE1C1h , 3 .text: 00061BF3 - 1DE4 8B 55 C8 mov edx, [ebp + var_38] .text: 00061BF6 - 1DE4 8B 44 95 D0 mov eax, [ebp + edx * 4 + var_30] .text: 00061BFA - 1DE4 D3 E0 shl eax, cl .text: 00061BFC - 1DE4 8B 4D C8 mov ecx, [ebp + var_38] .text: 00061BFF - 1DE4 C1 E1 03 shl ecx, 3 .text: 00061C02 - 1DE4 BA 20 00 00 00 mov edx, 20h ; ' ' .text: 00061C07 - 1DE4 2B D1 sub edx, ecx .text: 00061C09 - 1DE4 8B 4D C8 mov ecx, [ebp + var_38] .text: 00061C0C - 1DE4 8B 74 8D D0 mov esi, [ebp + ecx * 4 + var_30] .text: 00061C10 - 1DE4 8B CA mov ecx, edx .text: 00061C12 - 1DE4 D3 EE shr esi, cl .text: 00061C14 - 1DE4 0B C6 or eax, esi .text: 00061C16 - 1DE4 8B 55 C8 mov edx, [ebp + var_38] .text: 00061C19 - 1DE4 89 44 95 D0 mov [ebp + edx * 4 + var_30], eax .text: 00061C19 ; } / / starts at 61BE5 .text: 00061C1D - 1DE4 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00061C24 - 1DE4 EB 3E jmp short loc_61C64 .text: 00061C24 |
异常filter执行之后的代码exception
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | .text: 00061C2C ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00061C2C .text: 00061C2C loc_61C2C: .text: 00061C2C ; __except(loc_61C26) / / owned by 61BE5 .text: 00061C2C - 1DE4 8B 65 E8 mov esp, [ebp + ms_exc.old_esp] .text: 00061C2F 004 8B 4D C8 mov ecx, [ebp + var_38] .text: 00061C32 004 C1 E1 03 shl ecx, 3 .text: 00061C35 004 8B 45 C8 mov eax, [ebp + var_38] .text: 00061C38 004 8B 54 85 D0 mov edx, [ebp + eax * 4 + var_30] .text: 00061C3C 004 D3 EA shr edx, cl .text: 00061C3E 004 8B 45 C8 mov eax, [ebp + var_38] .text: 00061C41 004 C1 E0 03 shl eax, 3 .text: 00061C44 004 B9 20 00 00 00 mov ecx, 20h ; ' ' .text: 00061C49 004 2B C8 sub ecx, eax .text: 00061C4B 004 8B 45 C8 mov eax, [ebp + var_38] .text: 00061C4E 004 8B 44 85 D0 mov eax, [ebp + eax * 4 + var_30] .text: 00061C52 004 D3 E0 shl eax, cl .text: 00061C54 004 0B D0 or edx, eax .text: 00061C56 004 8B 4D C8 mov ecx, [ebp + var_38] .text: 00061C59 004 89 54 8D D0 mov [ebp + ecx * 4 + var_30], edx .text: 00061C5D 004 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00061C5D |
我们对比分析只有,发现两端代码有很多相似之处,但是有一个循环左移与循环右移的区别
那就是说,这段代码对 行位移 的关键操作进行了变换,循环左移 改为了循环右移
2.4.4、列混淆 sub_6100F
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | / / attributes: thunk int __cdecl sub_6100F( int a1) { return sub_61E50(a1); } int __cdecl sub_61E50( int a1) { char v1; / / bl char v2; / / bl char v3; / / bl int m; / / [esp + Ch] [ebp - 40h ] int k; / / [esp + 10h ] [ebp - 3Ch ] int j; / / [esp + 14h ] [ebp - 38h ] int i; / / [esp + 18h ] [ebp - 34h ] char v9[ 44 ]; / / [esp + 20h ] [ebp - 2Ch ] BYREF v9[ 0 ] = 2 ; v9[ 1 ] = 3 ; v9[ 2 ] = 1 ; v9[ 3 ] = 1 ; v9[ 4 ] = 1 ; v9[ 5 ] = 2 ; v9[ 6 ] = 3 ; v9[ 7 ] = 1 ; v9[ 8 ] = 1 ; v9[ 9 ] = 1 ; v9[ 10 ] = 2 ; v9[ 11 ] = 3 ; v9[ 12 ] = 3 ; v9[ 13 ] = 1 ; v9[ 14 ] = 1 ; v9[ 15 ] = 2 ; for ( i = 0 ; i < 4 ; + + i ) { for ( j = 0 ; j < 4 ; + + j ) v9[ 4 * i + 24 + j] = * (_BYTE * )(a1 + 4 * i + j); } for ( k = 0 ; k < 4 ; + + k ) { for ( m = 0 ; m < 4 ; + + m ) { v1 = sub_61118(v9[ 4 * k], v9[m + 0x18 ]); v2 = sub_61118(v9[ 4 * k + 1 ], v9[m + 0x1C ]) ^ v1; v3 = sub_61118(v9[ 4 * k + 2 ], v9[m + 0x20 ]) ^ v2; * (_BYTE * )(a1 + 4 * k + m) = sub_61118(v9[ 4 * k + 3 ], v9[m + 0x24 ]) ^ v3; } } return 0 ; } |
里面有四个 sub_61118 的操作,继续跟进下去,发现这个函数做了一些改变,对Buf2进行了修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | / / attributes: thunk char __cdecl sub_61118(unsigned __int8 a1, char a2) { return sub_61D50(a1, a2); } char __cdecl sub_EB1D50(unsigned __int8 a1, char a2) { int v3; / / [esp + 10h ] [ebp - 24h ] int i; / / [esp + 14h ] [ebp - 20h ] char v5; / / [esp + 1Bh ] [ebp - 19h ] v5 = 0 ; for ( i = 0 ; i < 8 ; + + i ) { if ( (a1 & 1 ) ! = 0 ) v5 ^ = a2; v3 = a2 & 0x80 ; a2 * = 2 ; if ( v3 ) a2 ^ = 0x1Bu ; a1 >> = 1 ; } return v5; } |
sub_EB1D50看伪代码,的确不能发现问题,需要回到 Graphics里看,有一段异常代码处理
1 2 3 4 5 6 7 8 9 10 11 | .text: 00061DAC ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00061DAC .text: 00061DAC loc_61DAC: .text: 00061DAC ; __except filter / / owned by 61D98 .text: 00061DAC 038 8B 45 EC mov eax, [ebp + ms_exc.exc_ptr] .text: 00061DAF 038 50 push eax .text: 00061DB0 03C E8 94 F4 FF FF call sub_61249 .text: 00061DB0 .text: 00061DB5 03C 83 C4 04 add esp, 4 .text: 00061DB8 038 C3 retn .text: 00061DB8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .text: 00061D98 ; __try { / / __except at loc_61DB9 .text: 00061D98 038 C7 45 FC 00 00 00 00 mov [ebp + ms_exc.registration.TryLevel], 0 .text: 00061D9F 038 33 DB xor ebx, ebx .text: 00061DA1 038 F7 F3 div ebx .text: 00061DA1 ; } / / starts at 61D98 .text: 00061DA3 038 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00061DAA 038 EB 17 jmp short loc_61DC3 .text: 00061DAA .text: 00061DB9 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00061DB9 .text: 00061DB9 loc_61DB9: .text: 00061DB9 ; __except(loc_61DAC) / / owned by 61D98 .text: 00061DB9 038 8B 65 E8 mov esp, [ebp + ms_exc.old_esp] .text: 00061DBC 038 C7 45 FC FE FF FF FF mov [ebp + ms_exc.registration.TryLevel], 0FFFFFFFEh .text: 00061DBC |
这个异常代码做了什么?
1 2 3 4 5 6 7 8 9 10 11 | / / attributes: thunk int sub_61249() { return sub_623D0(); } int sub_623D0() { Buf2[ 0x10 ] = 0xF4 ; Buf2[ 0x11 ] = 0xF2 ; return 1 ; } |
Buf2就是用户SN经过AES加密之后的密文:
577CF56D56967745B0BDA1C789A5ABDCF4F24BFEBEF5F55C4D30420F2B3BE6CB
这个方法是将两个字节进行了替换,上面是经过替换之后的内容,而最初的内容是
577CF56D56967745B0BDA1C789A5ABDCF2F44BFEBEF5F55C4D30420F2B3BE6CB
以上代码全部分析完成
三、回顾整个代码框架
3.1、获取用户输入SN
3.2、计算内置字符串的MD5
key = gMD5("Enj0y_1t_4_fuuuN")
3.3、计算AES密文
encryptContent = gAES(key,SN)
3.4、比对密文与内置的内容是否一致
encryptContent == 577CF56D56967745B0BDA1C789A5ABDCF2F44BFEBEF5F55C4D30420F2B3BE6CB
四、解密代码编写
待补充
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
赞赏
|
|
---|---|
|
|
|
感谢视频分享!
|
|
htg https://www.bilibili.com/video/BV1Vv4y1P7St?spm_id_from=333.999.0.0感谢分享,大佬讲解得很棒。 |