-
-
[原创]KCTF2023 第八题AI核心地带
-
2023-9-19 08:43 2291
-
- IDA加载,发现如下字符串
1 2 3 4 5 6 7 | / / 称为flag[ 10 ],看做 10 个 int 数 00402100 66 6C 61 67 7B 42 7A 63 5A 44 6E 66 4E 49 71 6D flag{BzcZDnfNIqm 00402110 51 43 74 6B 54 47 6C 77 4C 79 44 59 65 69 48 49 QCtkTGlwLyDYeiHI 00402120 6A 78 53 58 77 6B 52 4B 7A 70 46 50 76 7D 00 00 jxSXwkRKzpFPv}.. / / 称为check[ 5 ],取 20 字节,看做 5 个 int 00402130 43 61 6E 20 79 6F 75 20 63 72 61 63 6B 20 6D 65 Can you crack me 00402140 3F 5E 6F 6C 6F 5E 00 00 ?^olo^. |
- 输入最多1000位
1 2 3 4 5 | if ( input_len < = 0 || (input_len_1 = input_len - 1 , input [input_len - 1 ] ! = '\n' ) ) { v3 = 1 ; / / 错误 v27 = 1 ; } |
- 输入只能是数字
1 2 3 4 5 | input_num = input [v8]; if ( (unsigned int )(input_num - '0' ) < = 9 ) / / 只能输入数字 break ; v3 = 1 ; / / 这个数不能等于 1 v27 = 1 ; / / 这个数也不能等于 1 |
- 然后后面的代码很乱,看不懂在干啥,一度不想做这道题了
- 分析关键代码
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 | / / 根据下标和对应的数字,转换出一个 0 - 9 的数 v12 = (input_num + ( 0xFFFEC610 >> input_id % 31 )) % 0xA ; / / 奇数位,v12 = = 0 ;偶数位,v12! = 0 if ( (input_id & 1 ) ! = v12 < 1 ) v27 = 1 ; / / 将check数组中 5 个 int 做xor,得到 4 字节 = v28 v28 = v38 ^ HIDWORD(v37) ^ v37 ^ HIDWORD(v36) ^ v36; if ( v25 ) / / 如果是 input 最后一位,就进行校验 { / / v28的 (((bit0 ^ bit2) & 0x1F ) = = 0 ) / / v28的 (((bit1 ^ bit3) & 0x1F ) = = 0 ) if ( ((unsigned __int8)v28 ^ (unsigned __int8)(BYTE2(v38) ^ BYTE6(v37) ^ BYTE2(v37) ^ BYTE6(v36) ^ BYTE2(v36))) & 0x1F / / 也就是说这个条件,必须成立 || (HIBYTE(v28) ^ BYTE1(v28)) & 0x1F ) { v13 = - 1 ; / / v13等于 - 1 是成功状态 } goto LABEL_27; } / / 根据v12取出flag中的 4 字节,分别跟check的 5 个数xor v17 = v31[v12]; LODWORD(v36) = v17 ^ v36; / / 那 20 字节,跟选定的数字xor HIDWORD(v36) ^ = v17; LODWORD(v37) = v17 ^ v37; HIDWORD(v37) ^ = v17; / / xor后的数据要满足 if ( v12 > = 6 ) { if ( ((unsigned __int8)v28 ^ BYTE2(v28)) & 0x1F ) { LABEL_26: v13 = 9 ; / / 将当前v12重置为 9 goto LABEL_27; } v15 = 13 - v12; } else { v15 = 8 - v12; v16 = v28; / / = 0 } if ( (v16 << v15) + - 128 = = v14 << v15 ) / / 需要成立 goto LABEL_27; |
然后整理下大概逻辑:
check数组有初始状态
flag数组是始终不变的
根据input的每一位,计算出一个下标id,选取flag[id]
将check数组5个数,分别跟flag[id] xor,最终会进行一个校验因为xor具有偶数次消除的性质,所以先编写脚本,跑出最终结果需要flag中的[0] [2] [4] [8]
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 | / / 标记 10 层,每层选取的元素是谁 unsigned int id08[ 10 ] = { - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 }; / / 判断数组是否合规 1 = 合规 0 = 错误 int judge08(unsigned int * flag, unsigned int * check) { unsigned int final[ 5 ] = { 0 }; for ( int i = 0 ; i < 5 ; i + + ) final[i] = check[i]; for ( int i = 0 ; i < 10 ; i + + ) / / flag用 10 次 { if (id08[i] = = - 1 ) / / 不跟这个数参与xor continue ; for ( int j = 0 ; j < 5 ; j + + ) / / check中是 5 个 4 位数 { final[j] ^ = flag[i]; } } final[ 0 ] = final[ 0 ] ^ final[ 1 ] ^ final[ 2 ] ^ final[ 3 ] ^ final[ 4 ]; int bit0 = (final[ 0 ] & 0xFF000000 ) >> 24 ; int bit1 = (final[ 0 ] & 0xFF0000 ) >> 16 ; int bit2 = (final[ 0 ] & 0xFF00 ) >> 8 ; int bit3 = final[ 0 ] & 0xFF ; printf( "%x\n" , final[ 0 ]); int e = bit0 ^ bit2; int f = bit1 ^ bit3; printf( "%x %x\n" , e, f); if (((e & 0x1F ) = = 0 ) && ((f & 0x1F ) = = 0 )) { printf( "%x %x\n" , (e & 0x1F ), (f & 0x1F )); return 1 ; } return 0 ; } / / depth表示当前层级,一共遍历 39 层 void search08(unsigned int * flag, unsigned int * check, int depth) { if (depth = = 10 ) { / / printArr(id08, 10 ); if (judge08(flag, check)) { printf( "heihei\n" ); printArr(id08, 10 ); exit( 0 ); } return ; } for ( int i = 0 ; i < 2 ; i + + ) { if (i = = 0 ) id08[depth] = 1 ; else id08[depth] = - 1 ; search08(flag, check, depth + 1 ); } return ; } |
- 但是结果只选4位,分别对应0248,那样又不行,因为还有其他限制
8.1 奇数位,v12必须等于0;偶数位,v12不能等于0
8.2 若成立,flag下标就设定位为9
((unsigned __int8)v28 ^ BYTE2(v28)) & 0x1F
8.3 若成立,就能跳过flag设定为9
if ( (v16 << v15) + -128 == v14 << v15 )
goto LABEL_27; - 又因为 0248必须是奇数个数,4个奇数=偶数。135679都是偶数,所以input是偶数位。
然后就编码跑数据
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 | int judge08_2(unsigned int * flag, unsigned int * check, int index) { for ( int j = 0 ; j < 5 ; j + + ) / / check中是 5 个 4 位数 { check[j] ^ = flag[index]; } int num = check[ 0 ] ^ check[ 1 ] ^ check[ 2 ] ^ check[ 3 ] ^ check[ 4 ]; int bit0 = (num & 0xFF000000 ) >> 24 ; int bit1 = (num & 0xFF0000 ) >> 16 ; int bit2 = (num & 0xFF00 ) >> 8 ; int bit3 = num & 0xFF ; / / printf( "%x\n" , num); int e = bit0 ^ bit2; int f = bit1 ^ bit3; / / printf( "%x %x\n" , e, f); if (((e & 0x1F ) = = 0 ) && ((f & 0x1F ) = = 0 )) { / / printf( "%x %x\n" , (e & 0x1F ), (f & 0x1F )); return 1 ; } return 0 ; } void ctf08() { unsigned int check[] = { 0x43616e20 , 0x796f7520 , 0x63726163 , 0x6b206d65 , 0x3f5e6f6c }; unsigned int flag[] = { 0x427a635a , 0x446e664e , 0x49716d51 , 0x43746b54 , 0x476c774c , 0x79445965 , 0x6948496a , 0x78535877 , 0x6b524b7a , 0x70465076 }; for ( int i = 0 ; i < 1000 ; i + + ) { int j = 0 ; for (; j < 10 ; j + + ) / / 从 0 - 9 遍历 { int v12 = (( 0xFFFEC610 >> i % 31 ) + ( 0x30 + j)) % 0xA ; if ((i & 1 ) ! = v12 < 1 ) / / 过滤掉不符合的数字 continue ; if (v12 = = 0 ) / / i是奇数位 { for ( int k = 0 ; k < 5 ; k + + ) / / check中是 5 个 4 位数 { check[k] ^ = flag[v12]; / / 其实也就是 0 位 } printf( "id=%d num=%d v12=%d\n" , i, j, v12); break ; } int num = check[ 0 ] ^ check[ 1 ] ^ check[ 2 ] ^ check[ 3 ] ^ check[ 4 ]; / / 75 73 1D 00 = 0x001d7375 int al = (num & 0xFF00 ) >> 8 ; / / 73 int dl = (num & 0xFF000000 ) >> 24 ; int v15; if (v12 > = 6 ) { int e = al ^ dl; if ((e & 0x1F ) ! = 0 ) continue ; v15 = 13 - v12; al = num & 0xFF ; dl = (num & 0xFF0000 ) >> 16 ; } else v15 = 8 - v12; int a = (al << v15) & 0xff ; int b = (dl << v15) & 0xff ; if (a = = (( 128 + b) & 0xff )) { printf( "id=%d num=%d v12=%d\n" , i, j, v12); if (judge08_2(flag, check, v12)) { printf( "hahaha" ); exit( 0 ); } break ; } } if (j = = 10 ) / / 没找到数字的话 { for ( int k = 0 ; k < 5 ; k + + ) / / check中是 5 个 4 位数 { check[j] ^ = flag[ 9 ]; / / 其实也就是 0 位 } printf( "id=%d [9]\n" , i); } } / / search08(flag, check, 0 ); } |
- 跑出来的数据,再手动根据0248原则筛选,最后发现从486位截断不要9就行了。。。
582606981190746395118531851185249089744027265368693769576937697816165851808443150195011501950410798490871663488927792799277958360668112074539521851185318514909974002766535869476937695769681626582180144305010501950115040079949037162348792789277927995826067811907483951185218511853490897410272653986937694769376988161658318084433501950105019504207984904716634829277927892779584606681100745395418511852185149009740027365358697769376947696816365821809443050125019501050400790490371673487927
[培训]科锐逆向工程师培训 48期预科班将于 2023年10月13日 正式开班
赞赏
他的文章
[原创]看雪2023 第三题 秘密计划
3542