-
-
[原创]KCTF2023第二题 CN星际基地
-
2023-9-5 09:02 3861
-
- 先确定input长度为156
1 2 3 4 5 6 7 | if ( input_len ! = 156 ) / / 输入长度 156 { LABEL_16: v12 = fun_printf(std::cout, "fail" ); std::basic_ostream<char,std::char_traits<char>>::operator<<(v12, sub_1400030E0); goto LABEL_233; } |
- 然后将156分成4行39列,每一列从上到下识别为一个整数
1 2 3 4 5 6 7 | v20 = errno(); v21 = v20; v22 = (const char * )& Str ; if ( v149 > = 0x10 ) v22 = Str ; * v20 = 0 ; n4First = strtol(v22, &EndPtr, 10 ); |
- 如果数据中不存在2,并且数值为1111就fail
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 | if ( !MaxCount || (v27 = memchr(v24, '2' , MaxCount)) = = 0i64 || v27 - (_BYTE * )v24 = = - 1 ) { v28 = errno(); v29 = v28; v30 = (const char * )& Str ; if ( v149 > = 0x10 ) v30 = Str ; * v28 = 0 ; v31 = strtol(v30, &v146, 2 ); / / if ( v30 = = v146 ) goto LABEL_245; if ( * v29 = = 34 ) { std::_Xout_of_range( "stoi argument out of range" ); __debugbreak(); LABEL_245: std::_Xinvalid_argument( "invalid stoi argument" ); __debugbreak(); LABEL_246: std::_Xout_of_range( "stoi argument out of range" ); __debugbreak(); LABEL_247: std::_Xinvalid_argument( "invalid stoi argument" ); __debugbreak(); LABEL_248: sub_1400012C0( 0x80004005 ); } if ( v31 = = 15 ) { v126 = fun_printf(std::cout, "Fail" ); std::basic_ostream<char,std::char_traits<char>>::operator<<(v126, sub_1400030E0); goto LABEL_228; } v26 = v149; v25 = Str ; } |
- 如果数值中存在2,但是数值是2222就fail
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 | v41 = memchr(str4First_1, '2' , MaxCount); / / 又查找 2 。。。 if ( v41 ) { if ( v41 - (_BYTE * )str4First_1 ! = - 1 ) { v42 = 0 ; v43 = 0 ; v44 = * ((signed int * )Unicode4First - 4 ); LODWORD(v45) = 0 ; if ( (signed int )v44 < = 0 ) { v13 = v134; } else { do { v46 = (signed int )v45; if ( Unicode4First[v46] = = '2' ) { if ( !v43 ) { v43 = 1 ; if ( (( 1 - * ((_DWORD * )Unicode4First - 2 )) | ( * ((_DWORD * )Unicode4First - 3 ) - (signed int )v44)) < 0 ) { sub_140002D70(&v150, v44); Unicode4First = v150; } } Unicode4First[v46] = '1' ; + + v42; } v45 = (v46 * 2 + 2 ) >> 1 ; } while ( (signed int )v45 < (signed int )v44 ); n4First = v138; if ( v43 ) { if ( (signed int )v44 > * ((_DWORD * )Unicode4First - 3 ) ) sub_1400012C0( 0x80070057 ); * ((_DWORD * )Unicode4First - 4 ) = v44; Unicode4First[v44] = 0 ; } if ( v42 = = 4 ) { v62 = fun_printf(std::cout, "Fail" ); std::basic_ostream<char,std::char_traits<char>>::operator<<(v62, sub_1400030E0); goto LABEL_225; } v13 = v134; } } |
- 并且39个数中,后面的数不能小于等于前面的数
1 2 | if ( n4First < = n4FirstLast ) break ; |
- 然后是限制输入只能是0 1 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 28 29 30 31 32 | if ( + + v0 > = 39 ) / / v0进来是 0 ,所以说实质上,是遍历 39 轮,每轮处理 4 字节 { v57 = 0 ; if ( input_len_1 ) { v58 = 0i64 ; do { v59 = v57 / 39 ; / / 0 1 2 3 v60 = v57 % 39 ; / / 0 1 2 ... 38 v61 = input_2; if ( v153 > = 0x10 ) v61 = (void * * )input_2[ 0 ]; switch ( * ((_BYTE * )v61 + v58) ) { case 0x30 : * (_DWORD * )( * (_QWORD * )(qword_1400098A8 + 8i64 * v59) + 4i64 * v60) = 0 ; break ; case 0x31 : * (_DWORD * )( * (_QWORD * )(qword_1400098A8 + 8i64 * v59) + 4i64 * v60) = 1 ; break ; case 0x32 : * (_DWORD * )( * (_QWORD * )(qword_1400098A8 + 8i64 * v59) + 4i64 * v60) = - 1 ; break ; default: goto LABEL_16; } + + v57; + + v58; } while ( v57 < input_len_1 ); } |
- 限制每行1和2的数量必须相同
1 2 3 4 5 6 7 8 9 10 11 12 13 | if ( v65 = = v66 ) / / 他们俩为啥都是 0 呢 { * * (_DWORD * * )(v64 + qword_1400098B8) = 0 ; } else { v96 = - 1 ; if ( v65 < v66 ) v96 = 1 ; * * (_DWORD * * )(v64 + qword_1400098B8) = v96; } if ( v67 ) / / 这里还是要求每行 1 和 2 的数量相等 goto LABEL_16; |
- 然后就是计算MD5,转化为小写字符校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | v139 = 0 ; / / 直接从这里开始分析! v108 = 0i64 ; v144 = 0i64 ; v140 = 0x67452301 ; v141 = 0xEFCDAB89 ; v142 = 0x98BADCFE ; v143 = 0x10325476 ; v109 = (char * )input_2; if ( v153 > = 0x10 ) / / 0x9F v109 = (char * )input_2[ 0 ]; fun_md5init((__int64)&v139, v109, input_len_1); plain_md5 = fun_md5update((__int64)&v139); . . . v121 = * (_QWORD * )v136 ! = 32i64 || memcmp(v118, "aac82b7ad77ab00dcef90ac079c9490d" , 0x20ui64 ); |
然后就是写脚本瞎跑,1天也没跑出来。然后看到网页里有提示。。
[作者提示:序列号转换后的数值数组:
1、列向量组里不存在相反值,如[1,-1,1,0]与[-1,1,-1,0]不能同时存在
2、每个行向量里的 0 的数量 占 1/3
2023/9/3 20:55
]根据提示重新写脚本跑数据
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 | / / 标记 39 层,每层选取的元素是谁 unsigned int id02[ 39 ] = { - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 }; / / 判断差值是否合规 1 = 合规 0 = 错误 int judge( int value) { int precision = 9 ; if ((value < 0 ) && ( 0 - value > precision)) return 0 ; if ((value > 0 ) && (value > precision)) return 0 ; return 1 ; } / / depth表示当前层级,一共遍历 39 层 void search02(unsigned int * arr, int depth, int qian, int bai, int shi, int ge) { if (depth = = 39 ) { if (qian = = 0 && bai = = 0 && shi = = 0 && ge = = 0 ) { unsigned int final39[ 39 ] = { 0 }; for ( int i = 0 ; i < 39 ; i + + ) final39[i] = * (arr + 2 * i + id02[i]); / / 获取当前数值 Bubble_sort(final39, 39 ); / / 对final39数组排序 / / 构造 156 位字符串,计算md5 unsigned char * input = (unsigned char * )malloc( 4 * 39 + 1 ); memset( input , '0' , 4 * 39 ); input [ 4 * 39 ] = 0 ; for ( int i = 0 ; i < 39 ; i + + ) { int value = final39[i]; / / 从对元素中取一个 int bit0 = value / 1000 ; / / 千位 第一行 int bit1 = value % 1000 / 100 ; / / 百位 第二行 int bit2 = value % 100 / 10 ; / / int bit3 = value % 10 ; input [i] = bit0 + '0' ; input [i + 39 ] = bit1 + '0' ; input [i + 39 * 2 ] = bit2 + '0' ; input [i + 39 * 3 ] = bit3 + '0' ; } char * res = getMd5( input ); if (strcmp(res, "aac82b7ad77ab00dcef90ac079c9490d" ) = = 0 ) { printf( "%s\n" , input ); printf( "%s\n" , res); free( input ); free(res); exit( 0 ); } free( input ); free(res); } } for ( int i = 0 ; i < 2 ; i + + ) { int value = * (arr + 2 * depth + i); / / 从对元素中取一个 int bit0 = value / 1000 ; / / 千位 第一行 int bit1 = value % 1000 / 100 ; / / 百位 第二行 int bit2 = value % 100 / 10 ; / / int bit3 = value % 10 ; if (bit0 = = 2 ) bit0 = - 1 ; if (bit1 = = 2 ) bit1 = - 1 ; if (bit2 = = 2 ) bit2 = - 1 ; if (bit3 = = 2 ) bit3 = - 1 ; if (judge(qian + bit0) && judge(bai + bit1) && judge(shi + bit2) && judge(ge + bit3)) { id02[depth] = i; search02(arr, depth + 1 , qian + bit0, bai + bit1, shi + bit2, ge + bit3); id02[depth] = - 1 ; } } return ; } void ctf02() { / / 准备对立数组 unsigned int arr[ 39 ][ 2 ] = { { 1 , 2 },{ 10 , 20 },{ 11 , 22 },{ 12 , 21 },{ 100 , 200 },{ 101 , 202 },{ 102 , 201 },{ 110 , 220 },{ 111 , 222 },{ 112 , 221 }, { 120 , 210 },{ 121 , 212 },{ 122 , 211 },{ 1000 , 2000 },{ 1001 , 2002 },{ 1002 , 2001 },{ 1010 , 2020 },{ 1011 , 2022 },{ 1012 , 2021 },{ 1020 , 2010 }, { 1021 , 2012 },{ 1022 , 2011 },{ 1100 , 2200 },{ 1101 , 2202 },{ 1102 , 2201 },{ 1110 , 2220 },{ 1112 , 2221 },{ 1120 , 2210 },{ 1121 , 2212 },{ 1122 , 2211 }, { 1200 , 2100 },{ 1201 , 2102 },{ 1202 , 2101 },{ 1210 , 2120 },{ 1211 , 2122 },{ 1212 , 2121 },{ 1220 , 2110 },{ 1221 , 2112 },{ 1222 , 2111 } }; search02((unsigned int * )arr, 0 , 0 , 0 , 0 , 0 ); } |
- 拿到flag
000000000000011111111111112222222222222000011111111100001122222220000011222222011100011122200120200112220112202001122101201201201212001212020120121202010201
[培训]科锐逆向工程师培训 48期预科班将于 2023年10月13日 正式开班
赞赏
他的文章
[原创]看雪2023 第三题 秘密计划
3539