-
-
[原创] 看雪 2024 KCTF 大赛 第九题 第一次接触
-
发表于: 2024-9-4 01:31 3303
-
第九题难得一见是基于方案一的(上一道还是第二题),着实缓了一口气
IDA打开,从main函数开始一点点看代码:
主函数,逻辑是读取输入、检查字符为数字和小写字母、长度是3的倍数;
然后将输入拆成等长的三份,分别由sub_456BCB、sub_4578CD、sub_456E78进行检查;
最后,还对原始输入做sub_4579F4的变换,然后与常量比较,通过后才输出成功。
从sub_4579F4连续追进去几层,根据数值常量(sub_45D890等),可以判定是计算md5然后输出hexencode,暂时不用去管。
注意一个情况:主函数没有任何对输入长度的判断。
sub_456BCB处理第一段输入,逐层查看,主要逻辑在两处:
sub_45CCA0只是将大写字母转为小写,对于本题的合法输入没有影响;
sub_45C360是简单的字符串匹配,所以,第一部分的前8个字符确定是hellocat
(注意这里是strncmp而不是strcmp,所以第一段的确切长度仍然不知道)(p.s. 第一次看的时候确实没有注意这种细节)。
sub_4578CD处理第二段输入,逐层查看,主要逻辑如下:
sub_45CBC0是从sub_455357进入并最终调用到的,从代码和最后的字符串看,功能是字符串倒序。
unknown_libname_75是从j_unknown_libname_94进入并最终调用到的。从return调用的函数名看,它的功能是把字符串转换为十进制整数。
dword_531004[0]是常量13,dword_531008是常量3。
结合以上,sub_45C400的功能是:将提供的字符串倒序并解析为十进制数字,然后要求每位数字相加之和等于13,且整体乘以3后等于150633606。
逆推一下,150633606除以3等于50211202,再倒序是20211205
,长度是8。
(那么,第二段输入的长度一定是8吗?这个问题稍后再回来看)
sub_456E78处理第三段输入,逐层查看,主要逻辑仍然在两个函数里:
先看sub_45C710,参数是一个长度为2的字符串,第一个字符是'A'、'B'、'C'、'D'或'a'、'b'、'c'、'd'之一(根据main函数的检查,这里只可能是小写字母),第二个字符是'1'、'2'、'3'、'4'之一。
这个函数的大部分路径都返回0,只有一条路径返回1,合理假设后者为正确路径(因为约束更多)。
byte_531EA0是4×4的方阵,初始值全部0;参数可以看作坐标,六个for循环分别检查在坐标位置填入1时其所在的行列和斜线上所有的位置都是0。
手动试一试,可以发现满足的填法只有两种:
两种是对称的,转换为坐标从上到下依次为:a2b4c1d3
和a3b1c4d2
再看sub_45C500,先检查参数字符串的长度不小于8(等于或者大于都是允许的);
然后,是顺序检查,索引0、2、4、8位置的字符要递增,否则dword_531E9C与任何一个值做了“|”运算后都不可能等于目标值。
但是,最后经过仔细计算可以发现,单纯按照这个函数的逻辑,无论后面四个sub_458232的返回值是什么,dword_531E9C都永远不可能等于0x10000039。
(看起来下面四个分支的 1 ^ 8 ^ 0x10 ^ 0x20 恰好等于 0x39,但是1是等于赋值而不是异或会消除掉0x10000000;另外如果要进入异或分支,sub_45C710需要返回0而不是1,也与前面的推断不符)
分析到这里,第一个怀疑是,会不会程序只检查最后的md5而不管前面的逻辑?.
假设三段长度都是8,可以拼出两个字符串"hellocat20211205a2b4c1d3"和"hellocat20211205a3b1c4d2",但是md5都不正确
然后怀疑题目有反调试或自修改或者init藏逻辑之类的导致逆向的函数不是真实逻辑,但是调试器试了一圈并没有,但是确认了前两部分能通过检查。
此时仍然存在输入长度无法确定的疑点。可能上一题是PWN的影响还未散去,半无意半有意的点开了sub_45C500引用全局变量:
最终用于比较的dword_531E9C变量恰好位于&Str+8的位置,所以只要第三部分的长度大于等于9,就会覆盖dword_531E9C变量!
最终的比较 dword_531E9C == 0x10000039 ,0x39是可见字符'9',所以第三部分的长度应该是9(如果大于9,会覆盖第二个字节导致结果不正确),且最后一个字符为'9'。同时,四处sub_458232都要走严格路径返回1,避免进入异或分支破坏dword_531E9C。
由于输入分成等长的三份,所以每份的长度都需要是9。第一部分前8个字符固定,第9个字符待定;第二部分,末尾可以补0,不影响倒序后十进制解析的结果。
对未知的部分做一个简单的md5爆破:
可以找到唯一满足的结果:
最终正确的serial:(总长度:27 (9*3))
int
__cdecl main_0(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
*Str1;
// [esp+D0h] [ebp-188h]
char
*Source;
// [esp+DCh] [ebp-17Ch]
char
*v6;
// [esp+E8h] [ebp-170h]
char
*Destination;
// [esp+F4h] [ebp-164h]
int
Count;
// [esp+100h] [ebp-158h]
signed
int
i;
// [esp+10Ch] [ebp-14Ch]
signed
int
v10;
// [esp+118h] [ebp-140h]
char
Str[304];
// [esp+124h] [ebp-134h] BYREF
__CheckForDebuggerJustMyCode(&unk_535028);
sub_45548D(
"请输入序列号:"
);
j__memset(Str, 0, 0x12Cu);
j_scanf(
"%[^\n]"
, (
char
)Str);
v10 = j__strlen(Str);
if
( !(v10 % 3) && v10 )
{
for
( i = 0; i < v10; ++i )
{
if
( (Str[i] <
'a'
|| Str[i] >
'z'
) && (Str[i] <
'0'
|| Str[i] >
'9'
) )
goto
LABEL_3;
}
Count = v10 / 3;
Destination = (
char
*)j__malloc(__CFADD__(v10 / 3, 1) ? -1 : v10 / 3 + 1);
v6 = (
char
*)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
Source = (
char
*)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
j__memset(Destination, 0, v10 / 3 + 1);
j__memset(v6, 0, v10 / 3 + 1);
j__memset(Source, 0, v10 / 3 + 1);
j__strncpy(Destination, Str, v10 / 3);
j__strncpy(v6, &Str[Count], Count);
j__strncpy(Source, &Str[2 * Count], Count);
if
( sub_456BCB(Destination) && sub_4578CD(v6) && sub_456E78(Source) )
{
Str1 = (
char
*)sub_4579F4(Str);
if
( !j__strcmp(Str1,
"40d511825ecbc207eb6ef9a7b1c6e34b"
) )
sub_45548D(
"Success~\n"
);
else
sub_45548D(
"WR0NG!\n"
);
j__free(Str1);
}
else
{
sub_45548D(
"WR0NG!\n"
);
}
j__free(Destination);
j__free(v6);
j__free(Source);
return
0;
}
else
{
LABEL_3:
sub_45548D(
"WRONG!\n"
);
return
0;
}
}
int
__cdecl main_0(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
*Str1;
// [esp+D0h] [ebp-188h]
char
*Source;
// [esp+DCh] [ebp-17Ch]
char
*v6;
// [esp+E8h] [ebp-170h]
char
*Destination;
// [esp+F4h] [ebp-164h]
int
Count;
// [esp+100h] [ebp-158h]
signed
int
i;
// [esp+10Ch] [ebp-14Ch]
signed
int
v10;
// [esp+118h] [ebp-140h]
char
Str[304];
// [esp+124h] [ebp-134h] BYREF
__CheckForDebuggerJustMyCode(&unk_535028);
sub_45548D(
"请输入序列号:"
);
j__memset(Str, 0, 0x12Cu);
j_scanf(
"%[^\n]"
, (
char
)Str);
v10 = j__strlen(Str);
if
( !(v10 % 3) && v10 )
{
for
( i = 0; i < v10; ++i )
{
if
( (Str[i] <
'a'
|| Str[i] >
'z'
) && (Str[i] <
'0'
|| Str[i] >
'9'
) )
goto
LABEL_3;
}
Count = v10 / 3;
Destination = (
char
*)j__malloc(__CFADD__(v10 / 3, 1) ? -1 : v10 / 3 + 1);
v6 = (
char
*)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
Source = (
char
*)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
j__memset(Destination, 0, v10 / 3 + 1);
j__memset(v6, 0, v10 / 3 + 1);
j__memset(Source, 0, v10 / 3 + 1);
j__strncpy(Destination, Str, v10 / 3);
j__strncpy(v6, &Str[Count], Count);
j__strncpy(Source, &Str[2 * Count], Count);
if
( sub_456BCB(Destination) && sub_4578CD(v6) && sub_456E78(Source) )
{
Str1 = (
char
*)sub_4579F4(Str);
if
( !j__strcmp(Str1,
"40d511825ecbc207eb6ef9a7b1c6e34b"
) )
sub_45548D(
"Success~\n"
);
else
sub_45548D(
"WR0NG!\n"
);
j__free(Str1);
}
else
{
sub_45548D(
"WR0NG!\n"
);
}
j__free(Destination);
j__free(v6);
j__free(Source);
return
0;
}
else
{
LABEL_3:
sub_45548D(
"WRONG!\n"
);
return
0;
}
}
size_t
__cdecl sub_45CCA0(
char
*Str)
{
size_t
result;
// eax
size_t
i;
// [esp+D0h] [ebp-8h]
result = __CheckForDebuggerJustMyCode(&unk_535028);
if
( Str )
{
dword_531E90 = sub_45D4E0(0);
for
( i = 0; ; ++i )
{
result = j__strlen(Str);
if
( i >= result )
break
;
if
( Str[i] >=
'A'
&& Str[i] <=
'Z'
)
Str[i] += 32;
}
}
return
result;
}
BOOL
__cdecl sub_45C360(
char
*Str1)
{
__CheckForDebuggerJustMyCode(&unk_535028);
sub_455816(Str1);
return
j__strncmp(Str1,
"hellocat"
, 8u) == 0;
}
size_t
__cdecl sub_45CCA0(
char
*Str)
{
size_t
result;
// eax
size_t
i;
// [esp+D0h] [ebp-8h]
result = __CheckForDebuggerJustMyCode(&unk_535028);
if
( Str )
{
dword_531E90 = sub_45D4E0(0);
for
( i = 0; ; ++i )
{
result = j__strlen(Str);
if
( i >= result )
break
;
if
( Str[i] >=
'A'
&& Str[i] <=
'Z'
)
Str[i] += 32;
}
}
return
result;
}
BOOL
__cdecl sub_45C360(
char
*Str1)
{
__CheckForDebuggerJustMyCode(&unk_535028);
sub_455816(Str1);
return
j__strncmp(Str1,
"hellocat"
, 8u) == 0;
}
BOOL
__cdecl sub_45C400(
char
*Str)
{
size_t
i;
// [esp+D4h] [ebp-14h]
int
v3;
// [esp+E0h] [ebp-8h]
__CheckForDebuggerJustMyCode(&unk_535028);
sub_455357(Str);
v3 = 0;
for
( i = 0; i < j__strlen(Str); ++i )
v3 = v3 + Str[i] -
'0'
;
return
v3 == dword_531004[0] && dword_531008 * j_unknown_libname_94((
int
)Str) == 150633606;
}
char
*__cdecl sub_45CBC0(
char
*Str)
{
char
*result;
// eax
char
v2;
// [esp+D3h] [ebp-1Dh]
char
*i;
// [esp+DCh] [ebp-14h]
result = (
char
*)__CheckForDebuggerJustMyCode(&unk_535028);
if
( Str )
{
for
( i = &Str[j__strlen(Str) - 1]; ; --i )
{
result = i;
if
( i <= Str )
break
;
v2 = *Str;
*Str = *i;
*i = v2;
++Str;
}
}
if
( dword_531000 )
return
(
char
*)sub_456B21(
"std::reverse()"
, 1);
return
result;
}
// Microsoft VisualC universal runtime
int
__cdecl unknown_libname_75(
int
a1,
int
a2,
int
a3,
struct
__crt_locale_pointers *a4)
{
char
v5;
// [esp-10h] [ebp-14h] BYREF
int
v6;
// [esp-Ch] [ebp-10h]
int
v7;
// [esp-8h] [ebp-Ch]
int
v8;
// [esp-4h] [ebp-8h]
char
*v9;
// [esp+0h] [ebp-4h]
v8 = 1;
v7 = a3;
v9 = &v5;
j_unknown_libname_73(&v5, a1, a2);
return
__crt_strtox::parse_integer<unsigned
long
,__crt_strtox::c_string_character_source<
char
>>(a4, v5, v6, v7, v8);
}
BOOL
__cdecl sub_45C400(
char
*Str)
{
size_t
i;
// [esp+D4h] [ebp-14h]
int
v3;
// [esp+E0h] [ebp-8h]
__CheckForDebuggerJustMyCode(&unk_535028);
sub_455357(Str);
v3 = 0;
for
( i = 0; i < j__strlen(Str); ++i )
v3 = v3 + Str[i] -
'0'
;
return
v3 == dword_531004[0] && dword_531008 * j_unknown_libname_94((
int
)Str) == 150633606;
}
char
*__cdecl sub_45CBC0(
char
*Str)
{
char
*result;
// eax
char
v2;
// [esp+D3h] [ebp-1Dh]
char
*i;
// [esp+DCh] [ebp-14h]
result = (
char
*)__CheckForDebuggerJustMyCode(&unk_535028);
if
( Str )
{
for
( i = &Str[j__strlen(Str) - 1]; ; --i )
{
result = i;
if
( i <= Str )
break
;
v2 = *Str;
*Str = *i;
*i = v2;
++Str;
}
}
if
( dword_531000 )
return
(
char
*)sub_456B21(
"std::reverse()"
, 1);
return
result;
}
// Microsoft VisualC universal runtime
int
__cdecl unknown_libname_75(
int
a1,
int
a2,
int
a3,
struct
__crt_locale_pointers *a4)
{
char
v5;
// [esp-10h] [ebp-14h] BYREF
int
v6;
// [esp-Ch] [ebp-10h]
int
v7;
// [esp-8h] [ebp-Ch]
int
v8;
// [esp-4h] [ebp-8h]
char
*v9;
// [esp+0h] [ebp-4h]
v8 = 1;
v7 = a3;
v9 = &v5;
j_unknown_libname_73(&v5, a1, a2);
return
__crt_strtox::parse_integer<unsigned
long
,__crt_strtox::c_string_character_source<
char
>>(a4, v5, v6, v7, v8);
}
// attributes: thunk
int
__cdecl sub_456E78(
char
*Source)
{
return
sub_45C500(Source);
}
BOOL
__cdecl sub_45C500(
char
*Source)
{
__CheckForDebuggerJustMyCode(&unk_535028);
j__strcpy(&Str, Source);
if
( j__strlen(&Str) < 8 )
return
0;
dword_531E9C |= 0x10000000u;
if
( Str >= *(&Str + 2) )
dword_531E9C |= 0x88u;
if
( *(&Str + 2) >= *(&Str + 4) )
dword_531E9C |= 0x90u;
if
( *(&Str + 4) >= *(&Str + 6) )
dword_531E9C |= 0xA0u;
if
( !sub_458232((
int
)&Str) )
dword_531E9C = 1;
if
( !sub_458232((
int
)&unk_531E96) )
dword_531E9C ^= 8u;
if
( !sub_458232((
int
)&unk_531E98) )
dword_531E9C ^= 0x10u;
if
( !sub_458232((
int
)&unk_531E9A) )
dword_531E9C ^= 0x20u;
return
dword_531E9C == 0x10000039;
}
// attributes: thunk
int
__cdecl sub_458232(
int
a1)
{
return
sub_45C710(a1);
}
int
__cdecl sub_45C710(
char
*a1)
{
unsigned
int
ii;
// [esp+D0h] [ebp-8Ch]
unsigned
int
v3;
// [esp+DCh] [ebp-80h]
unsigned
int
n;
// [esp+E8h] [ebp-74h]
unsigned
int
v5;
// [esp+F4h] [ebp-68h]
unsigned
int
m;
// [esp+100h] [ebp-5Ch]
unsigned
int
v7;
// [esp+10Ch] [ebp-50h]
unsigned
int
k;
// [esp+118h] [ebp-44h]
unsigned
int
v9;
// [esp+124h] [ebp-38h]
int
j;
// [esp+130h] [ebp-2Ch]
int
i;
// [esp+13Ch] [ebp-20h]
int
v12;
// [esp+148h] [ebp-14h]
int
v13;
// [esp+154h] [ebp-8h]
__CheckForDebuggerJustMyCode(&unk_535028);
if
( *a1 <
'a'
|| *a1 >
'd'
)
{
if
( *a1 <
'A'
|| *a1 >
'D'
)
return
0;
v12 = *a1 - 65;
}
else
{
v12 = *a1 - 97;
}
if
( a1[1] <
'1'
|| a1[1] >
'4'
)
return
0;
v13 = 4 - (a1[1] - 48);
if
( byte_531EA0[4 * v13 + v12] )
return
0;
byte_531EA0[4 * v13 + v12] = 1;
for
( i = 0; i < 4; ++i )
{
if
( i != v12 && byte_531EA0[4 * v13 + i] )
return
0;
}
for
( j = 0; j < 4; ++j )
{
if
( j != v13 && byte_531EA0[4 * j + v12] )
return
0;
}
v9 = v13 - 1;
for
( k = v12 - 1; v9 < 4 && k < 4; --k )
{
if
( byte_531EA0[4 * v9 + k] )
return
0;
--v9;
}
v7 = v13 - 1;
for
( m = v12 + 1; v7 < 4 && m < 4; ++m )
{
if
( byte_531EA0[4 * v7 + m] )
return
0;
--v7;
}
v5 = v13 + 1;
for
( n = v12 - 1; v5 < 4 && n < 4; --n )
{
if
( byte_531EA0[4 * v5 + n] )
return
0;
++v5;
}
v3 = v13 + 1;
for
( ii = v12 + 1; v3 < 4 && ii < 4; ++ii )
{
if
( byte_531EA0[4 * v3 + ii] )
return
0;
++v3;
}
return
1;
}
// attributes: thunk
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!