-
-
[原创]看雪.深信服 2021 KCTF 春季赛 第四题 英雄救美 WP
-
发表于: 2021-5-15 01:20 6808
-
此题是一个控制台程序,随便输入后,没有错误信息提示。
程序的主业务流程全在主函数中,伪代码如下:
结构比较简单明了。先大致看了下,if
的三个条件应该是校验条件,下面似乎是校验无关。后详细看了下if
中的两个函数,才明白:程序的校验实际上是分两部分,一是数独部分,二是AES解密代码并显示出正常的对话框。
函数check_group_401240
是将输入通过一个字符表转换成一串0-9的数字。了解了这个,再看函数check_sudoku_401000
就很容易明白其功能了:将0-9数字依次填充到9*9矩阵的0值位上,并使用数独的规则进行数独求解成功是否的校验。
本来有长度要求再加上数独校验,就能出唯一解了,就无需后面的部分了。不知道作者是想增加点难度还是出于其它考虑,函数check_group_401240
的转换并不能由数独解反算出唯一输入,所以就有了后面的校验部分。
后面部分的校验主要是将原始输入取md5的hash作为密钥,对大小为0x610大小的加密代码进行AES解密,成功解密则调用这部分解密代码,猜测应该是提示输入正确。
具体看下函数check_group_401240
,其伪代码如下:
此函数通过搜索81个元素的字符表,并进行计算后将输入转换成0-9。具体的遍历输入的过程如下: 先检查输入字符是否是数字字符,如果是且与某个位置计数相加为0x39,则位置计数清零,字符表查询范围减少9个字符,否则则结束遍历,失败返回。如输入字符不是数字字符,则搜表得到该字符在表中位置值,经过模9加1计算后记录,作为数独的求解结果。这里在求解的时候要猜下作者意图,不然这部分的反解结果就太多了。根据对程序的意图理解,再结合数独的一些知识,可以猜测,输入中的数字字符是为了将输入分成9组,每组对应数独的一行;还可以猜测到,81个元素的字符表也是分成9组,每组9个字符,第i组的输入字符在出现在第i组的字符表中。
已知的数独如下:
为求解数独,随便搜了个在线的求解器,结果如下:
得到55个数字,有了这些数据,再加上上面部分的两个猜测,就可以直接反解输入了。代码如下:
反解得到:u$YBPf2pa]Dt4#QM^H4ic'j0`w2y{d-Zzo2%/n_s@+2<UW)e4AR;F.4=-qEkvC2
。最后的字符2
有没有,对于数独验证没有影响,最后试了下,加上2
后面的AES解密才正确。
printf(
"\t\t\t看雪CTF大赛\r\n"
);
printf(
"\t\t祝愿看雪CTF大赛越办越好\r\n"
);
printf(
"Serial: "
);
scanf_s(
"%s"
, &
input
,
81
);
length
=
strlen(&
input
);
if
( length <
=
64
&& check_group_401240(length, &
input
, v15)
=
=
1
&& check_sudoku_401000(v15, length
-
9
)
=
=
1
)
{
_mm_storeu_si128((__m128i
*
)&
hash
, (__m128i)
0i64
);
memset(&ctx,
0
,
0x58u
);
v14
=
0
;
v13
=
0
;
ctx
=
0x67452301
;
v10
=
0xEFCDAB89
;
v11
=
0x98BADCFE
;
v12
=
0x10325476
;
md5_update_4014E0((
int
)&
input
, (
int
)&ctx, length);
md5_final_4015B0((
int
)&
hash
, (__m128i
*
)&ctx);
subkey_401ED0(v4, (unsigned __int8
*
)&
hash
);
v8
=
(void (
*
)(void))VirtualAlloc(
0
,
0x620u
,
0x1000u
,
0x40u
);
v5
=
(__m128i
*
)v8;
v6
=
0x62
;
do
{
_mm_storeu_si128(v5, _mm_loadu_si128((__m128i
*
)((char
*
)v5
+
&unk_4181A0
-
(_UNKNOWN
*
)v8)));
AES_de_4028B0((
int
)v15, (unsigned __int8
*
)v5);
+
+
v5;
-
-
v6;
}
while
( v6 );
v8();
}
return
0
;
printf(
"\t\t\t看雪CTF大赛\r\n"
);
printf(
"\t\t祝愿看雪CTF大赛越办越好\r\n"
);
printf(
"Serial: "
);
scanf_s(
"%s"
, &
input
,
81
);
length
=
strlen(&
input
);
if
( length <
=
64
&& check_group_401240(length, &
input
, v15)
=
=
1
&& check_sudoku_401000(v15, length
-
9
)
=
=
1
)
{
_mm_storeu_si128((__m128i
*
)&
hash
, (__m128i)
0i64
);
memset(&ctx,
0
,
0x58u
);
v14
=
0
;
v13
=
0
;
ctx
=
0x67452301
;
v10
=
0xEFCDAB89
;
v11
=
0x98BADCFE
;
v12
=
0x10325476
;
md5_update_4014E0((
int
)&
input
, (
int
)&ctx, length);
md5_final_4015B0((
int
)&
hash
, (__m128i
*
)&ctx);
subkey_401ED0(v4, (unsigned __int8
*
)&
hash
);
v8
=
(void (
*
)(void))VirtualAlloc(
0
,
0x620u
,
0x1000u
,
0x40u
);
v5
=
(__m128i
*
)v8;
v6
=
0x62
;
do
{
_mm_storeu_si128(v5, _mm_loadu_si128((__m128i
*
)((char
*
)v5
+
&unk_4181A0
-
(_UNKNOWN
*
)v8)));
AES_de_4028B0((
int
)v15, (unsigned __int8
*
)v5);
+
+
v5;
-
-
v6;
}
while
( v6 );
v8();
}
return
0
;
_mm_storeu_si128((__m128i
*
)table, _mm_load_si128((const __m128i
*
)&xmmword_416280));
pos
=
0
;
_mm_storeu_si128((__m128i
*
)&table[
16
], _mm_load_si128((const __m128i
*
)&xmmword_4162A0));
idx1
=
0
;
v11
=
a1;
v10
=
a2;
out
=
a3;
table[
80
]
=
'q'
;
_mm_storeu_si128((__m128i
*
)&table[
32
], _mm_load_si128((const __m128i
*
)&xmmword_416270));
_mm_storeu_si128((__m128i
*
)&table[
48
], _mm_load_si128((const __m128i
*
)&xmmword_416290));
_mm_storeu_si128((__m128i
*
)&table[
64
], _mm_load_si128((const __m128i
*
)&xmmword_416260));
if
( a1 <
=
0
)
return
1
;
v5
=
0
;
while
(
1
)
{
ch_l
=
a2[idx1];
if
( ch_l >
0x30
&& ch_l <
=
0x39
)
break
;
idx2
=
v5;
if
( v5 >
=
0x51
)
return
0
;
while
( ch_l !
=
table[idx2] )
{
if
( (unsigned
int
)
+
+
idx2 >
=
0x51
)
return
0
;
}
v9
=
idx2
%
9
+
1
;
if
( v9
=
=
-
1
)
return
0
;
*
out
=
v9;
a2
=
v10;
+
+
pos;
+
+
out;
a1
=
v11;
LABEL_13:
if
(
+
+
idx1 >
=
a1 )
return
1
;
}
if
( pos
+
ch_l
=
=
0x39
)
{
pos
=
0
;
v5
+
=
9
;
goto LABEL_13;
}
return
-
1
;
_mm_storeu_si128((__m128i
*
)table, _mm_load_si128((const __m128i
*
)&xmmword_416280));
pos
=
0
;
_mm_storeu_si128((__m128i
*
)&table[
16
], _mm_load_si128((const __m128i
*
)&xmmword_4162A0));
idx1
=
0
;
v11
=
a1;
v10
=
a2;
out
=
a3;
table[
80
]
=
'q'
;
_mm_storeu_si128((__m128i
*
)&table[
32
], _mm_load_si128((const __m128i
*
)&xmmword_416270));
_mm_storeu_si128((__m128i
*
)&table[
48
], _mm_load_si128((const __m128i
*
)&xmmword_416290));
_mm_storeu_si128((__m128i
*
)&table[
64
], _mm_load_si128((const __m128i
*
)&xmmword_416260));
if
( a1 <
=
0
)
return
1
;
v5
=
0
;
while
(
1
)
{
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)