-
-
[原创]KCTF2021 春季赛 第四题 英雄救美 WP
-
发表于: 2021-5-16 21:53 10062
-
这道题本人认为还是比较有意思的,一道数独题,不过也有一些考虑不周的地方,数独的解应该是唯一的,不过注册码到数独的输入数据的转换有些瑕疵,所以不唯一,虽然用的是注册码的MD5解码代码,只有正确的注册码才能显示成功,但能过数独的多个注册码都能进入到SHELLCODE中去,表现为程序崩溃.
本文重点还是分析关于多个注册码能过数独验证的的部分,分析见后面程序中的注释:
主程序:
SN转数独数据:
数独数据填充及验证:
由以上分析可知:
1.本程序实为数独填充;
2.数独填充数据为SN中每个字符在每行数据(对应每组9个字符中的序号决定1-9);
3.每行数据由校验字符结束(本行所需数字个数+校验数字=9);
4.虽然能过数独验证的SN有多个,不过要能正确显示成功的SN还是要为预期,因为是用SN的MD5值对程序解码,只有预期SN解码出的代码才能正常运行,也就是说SN一定要有行结束验证符,且第N行所需的数字要在第N组字符转换表中.
在网上借用了个暴力解数独的程序,改了下,加上字符转换得KEYGEN程序:
得到SN:
:u$YBPf2pa]Dt4#QM^H4ic'j0`w2y{d-Zzo2%/n_s@+2<UW)e4AR;F.4=-qEkvC2
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
lenSn;
/
/
kr00_4
int
v4;
/
/
ecx
__m128i
*
v5;
/
/
esi
int
v6;
/
/
edi
void (
*
v8)(void);
/
/
[esp
+
Ch] [ebp
-
2CCh
]
int
v9[
22
];
/
/
[esp
+
10h
] [ebp
-
2C8h
] BYREF
int
iTab[
128
];
/
/
[esp
+
68h
] [ebp
-
270h
] BYREF
BYTE md5Sn[
16
];
/
/
[esp
+
268h
] [ebp
-
70h
] BYREF
char sn[
92
];
/
/
[esp
+
278h
] [ebp
-
60h
] BYREF
printf(
"\t\t\t看雪CTF大赛\r\n"
);
printf(
"\t\t祝愿看雪CTF大赛越办越好\r\n"
);
printf(
"Serial: "
);
/
/
输入SN
scanf_s(
"%s"
, sn);
/
/
取SN长度
lenSn
=
strlen(sn);
if
( lenSn <
=
64
&& sub_401240(lenSn, (
int
)sn, iTab)
=
=
1
&& sub_401000((
int
)iTab, lenSn
-
9
)
=
=
1
)
{
/
/
SN长度<
=
64
/
/
sub_401240 对输入的SN变换,转为长度
0x37
的数值大小(
1
-
9
)数组,输入正确返回
1
/
/
sub_401000 对输入的
0x37
个数据依次填入
9
*
9
的数独二维数组中为
0
的位置,并验证数独的正确性,成功返回
1
*
(_OWORD
*
)md5Sn
=
0i64
;
memset(v9,
0
, sizeof(v9));
v9[
5
]
=
0
;
v9[
4
]
=
0
;
v9[
0
]
=
0x67452301
;
v9[
1
]
=
0xEFCDAB89
;
v9[
2
]
=
0x98BADCFE
;
v9[
3
]
=
0x10325476
;
sub_4014E0((
int
)sn, (
int
)v9, lenSn);
sub_4015B0((
int
)md5Sn, (
int
)v9);
/
/
计算SN的MD5
sub_401ED0((
int
)iTab, v4, md5Sn);
/
/
用md5Sn初始化解密数据
v8
=
(void (
*
)(void))VirtualAlloc(
0
,
0x620u
,
0x1000u
,
0x40u
);
v5
=
(__m128i
*
)v8;
v6
=
98
;
do
{
*
v5
=
_mm_loadu_si128((__m128i
*
)((char
*
)v5
+
&unk_4181A0
-
(_UNKNOWN
*
)v8));
((void (__fastcall
*
)(
int
*
, __m128i
*
))loc_4028B0)(iTab, v5
+
+
);
/
/
对代码解码
-
-
v6;
}
while
( v6 );
v8();
/
/
执行解密后的代码;
/
/
由于SN转换数独数据的缺陷导致能过数独验证的SN本身就不是唯一,并且后面没有明确的正确性验证,因此解码数据不唯一,在非预期SN时程序会跑飞
}
return
0
;
}
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
lenSn;
/
/
kr00_4
int
v4;
/
/
ecx
__m128i
*
v5;
/
/
esi
int
v6;
/
/
edi
void (
*
v8)(void);
/
/
[esp
+
Ch] [ebp
-
2CCh
]
int
v9[
22
];
/
/
[esp
+
10h
] [ebp
-
2C8h
] BYREF
int
iTab[
128
];
/
/
[esp
+
68h
] [ebp
-
270h
] BYREF
BYTE md5Sn[
16
];
/
/
[esp
+
268h
] [ebp
-
70h
] BYREF
char sn[
92
];
/
/
[esp
+
278h
] [ebp
-
60h
] BYREF
printf(
"\t\t\t看雪CTF大赛\r\n"
);
printf(
"\t\t祝愿看雪CTF大赛越办越好\r\n"
);
printf(
"Serial: "
);
/
/
输入SN
scanf_s(
"%s"
, sn);
/
/
取SN长度
lenSn
=
strlen(sn);
if
( lenSn <
=
64
&& sub_401240(lenSn, (
int
)sn, iTab)
=
=
1
&& sub_401000((
int
)iTab, lenSn
-
9
)
=
=
1
)
{
/
/
SN长度<
=
64
/
/
sub_401240 对输入的SN变换,转为长度
0x37
的数值大小(
1
-
9
)数组,输入正确返回
1
/
/
sub_401000 对输入的
0x37
个数据依次填入
9
*
9
的数独二维数组中为
0
的位置,并验证数独的正确性,成功返回
1
*
(_OWORD
*
)md5Sn
=
0i64
;
memset(v9,
0
, sizeof(v9));
v9[
5
]
=
0
;
v9[
4
]
=
0
;
v9[
0
]
=
0x67452301
;
v9[
1
]
=
0xEFCDAB89
;
v9[
2
]
=
0x98BADCFE
;
v9[
3
]
=
0x10325476
;
sub_4014E0((
int
)sn, (
int
)v9, lenSn);
sub_4015B0((
int
)md5Sn, (
int
)v9);
/
/
计算SN的MD5
sub_401ED0((
int
)iTab, v4, md5Sn);
/
/
用md5Sn初始化解密数据
v8
=
(void (
*
)(void))VirtualAlloc(
0
,
0x620u
,
0x1000u
,
0x40u
);
v5
=
(__m128i
*
)v8;
v6
=
98
;
do
{
*
v5
=
_mm_loadu_si128((__m128i
*
)((char
*
)v5
+
&unk_4181A0
-
(_UNKNOWN
*
)v8));
((void (__fastcall
*
)(
int
*
, __m128i
*
))loc_4028B0)(iTab, v5
+
+
);
/
/
对代码解码
-
-
v6;
}
while
( v6 );
v8();
/
/
执行解密后的代码;
/
/
由于SN转换数独数据的缺陷导致能过数独验证的SN本身就不是唯一,并且后面没有明确的正确性验证,因此解码数据不唯一,在非预期SN时程序会跑飞
}
return
0
;
}
由分析发现SN到数独填充数据的转换过程是按行读取,共
9
行,每行为一组,由效验字符结束,即当前行所需填充的数据个数
+
效验字符
=
9
,并且SN到独数数据为SN字符在每组
9
个字符中的位置(
1
-
9
),不过分析中发现
2
个BUG:
1.
本来是一行一组转换字符表(一组
9
个),不过查表过程中没有限制查表仅在本组
9
个字符中,而是在之后的所有组中,也就是说仅最后一组应该是预期(唯一),之前的
8
组查表数据可以为之后的所有组中的字符,这样SN到数独数据的转换就不是唯一,且SN越靠前的重复的可能越高,最高一个数字可能有
9
个不同的值.
2.
每行数据的效验结束符其实并没有起作用,因为最后从输入数据序列中取出数据填充到数独表中时并没有考虑按对应的行读取,而只是整体作为一个一维数组来读取,所以行效验结束符其实可以没有,但因为预期有
9
个行结束符,而在填充数独数据时从总长度中减去了
9
个,因此在没有行结束验证字符时要在SN后随机加入字符(需在字符转换表中存在)才能过数独验证
由分析发现SN到数独填充数据的转换过程是按行读取,共
9
行,每行为一组,由效验字符结束,即当前行所需填充的数据个数
+
效验字符
=
9
,并且SN到独数数据为SN字符在每组
9
个字符中的位置(
1
-
9
),不过分析中发现
2
个BUG:
1.
本来是一行一组转换字符表(一组
9
个),不过查表过程中没有限制查表仅在本组
9
个字符中,而是在之后的所有组中,也就是说仅最后一组应该是预期(唯一),之前的
8
组查表数据可以为之后的所有组中的字符,这样SN到数独数据的转换就不是唯一,且SN越靠前的重复的可能越高,最高一个数字可能有
9
个不同的值.
2.
每行数据的效验结束符其实并没有起作用,因为最后从输入数据序列中取出数据填充到数独表中时并没有考虑按对应的行读取,而只是整体作为一个一维数组来读取,所以行效验结束符其实可以没有,但因为预期有
9
个行结束符,而在填充数独数据时从总长度中减去了
9
个,因此在没有行结束验证字符时要在SN后随机加入字符(需在字符转换表中存在)才能过数独验证
int
__usercall sub_401240@<eax>(
int
a1@<edx>,
int
a2@<ecx>,
int
*
a3)
{
int
v3;
/
/
ebx
int
v4;
/
/
esi
unsigned
int
v5;
/
/
edi
char v6;
/
/
al
signed
int
v7;
/
/
ecx
int
v9;
/
/
ecx
int
v10;
/
/
[esp
+
0h
] [ebp
-
64h
]
int
v11;
/
/
[esp
+
4h
] [ebp
-
60h
]
__int128 v13[
5
];
/
/
[esp
+
Ch] [ebp
-
58h
]
char v14;
/
/
[esp
+
5Ch
] [ebp
-
8h
]
v13[
0
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416280);
/
/
字符转换表,共
9
组,每组
9
个,每组数据对应数独数据的
9
行,每行数字的转换在当前组中的
9
个数中查表得到序数就是数独的填充数据
v3
=
0
;
v13[
1
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_4162A0);
v4
=
0
;
v11
=
a1;
v10
=
a2;
v14
=
'q'
;
v13[
2
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416270);
v13[
3
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416290);
v13[
4
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416260);
if
( a1 <
=
0
)
return
1
;
v5
=
0
;
while
(
1
)
{
v6
=
*
(_BYTE
*
)(v4
+
a2);
/
/
依次从SN中取字符
if
( v6 >
'0'
&& v6 <
=
'9'
)
/
/
如果是数字表示本行结束,不过本行数字个数
+
结束数字要等于
9
break
;
v7
=
v5;
if
( v5 >
=
0x51
)
return
0
;
while
( v6 !
=
*
((_BYTE
*
)v13
+
v7) )
{
if
( (unsigned
int
)
+
+
v7 >
=
0x51
)
/
/
不在表中的输入字符无效,返回
0
,不过这儿查表的数据没有限制在本组
9
个内,而是在当前组及之后的组内查询,可能会多解
return
0
;
}
v9
=
v7
%
9
+
1
;
/
/
序号为
0
-
8
,对应数独数据的
1
-
9
if
( v9
=
=
-
1
)
return
0
;
*
a3
=
v9;
/
/
保存一个数据
a2
=
v10;
+
+
v3;
+
+
a3;
/
/
下一个
a1
=
v11;
LABEL_13:
if
(
+
+
v4 >
=
a1 )
/
/
数据取完,返回成功
return
1
;
}
if
( v3
+
v6
=
=
'9'
)
/
/
行结束判断,本行数据个数
+
最后的数字要为
9
{
v3
=
0
;
v5
+
=
9
;
/
/
数字转换表指向下一组
goto LABEL_13;
}
return
-
1
;
}
int
__usercall sub_401240@<eax>(
int
a1@<edx>,
int
a2@<ecx>,
int
*
a3)
{
int
v3;
/
/
ebx
int
v4;
/
/
esi
unsigned
int
v5;
/
/
edi
char v6;
/
/
al
signed
int
v7;
/
/
ecx
int
v9;
/
/
ecx
int
v10;
/
/
[esp
+
0h
] [ebp
-
64h
]
int
v11;
/
/
[esp
+
4h
] [ebp
-
60h
]
__int128 v13[
5
];
/
/
[esp
+
Ch] [ebp
-
58h
]
char v14;
/
/
[esp
+
5Ch
] [ebp
-
8h
]
v13[
0
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416280);
/
/
字符转换表,共
9
组,每组
9
个,每组数据对应数独数据的
9
行,每行数字的转换在当前组中的
9
个数中查表得到序数就是数独的填充数据
v3
=
0
;
v13[
1
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_4162A0);
v4
=
0
;
v11
=
a1;
v10
=
a2;
v14
=
'q'
;
v13[
2
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416270);
v13[
3
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416290);
v13[
4
]
=
(__int128)_mm_load_si128((const __m128i
*
)&xmmword_416260);
if
( a1 <
=
0
)
return
1
;
v5
=
0
;
while
(
1
)
{
v6
=
*
(_BYTE
*
)(v4
+
a2);
/
/
依次从SN中取字符
if
( v6 >
'0'
&& v6 <
=
'9'
)
/
/
如果是数字表示本行结束,不过本行数字个数
+
结束数字要等于
9
break
;
v7
=
v5;
if
( v5 >
=
0x51
)
return
0
;
while
( v6 !
=
*
((_BYTE
*
)v13
+
v7) )
{
if
( (unsigned
int
)
+
+
v7 >
=
0x51
)
/
/
不在表中的输入字符无效,返回
0
,不过这儿查表的数据没有限制在本组
9
个内,而是在当前组及之后的组内查询,可能会多解
return
0
;
}
v9
=
v7
%
9
+
1
;
/
/
序号为
0
-
8
,对应数独数据的
1
-
9
if
( v9
=
=
-
1
)
return
0
;
*
a3
=
v9;
/
/
保存一个数据
a2
=
v10;
+
+
v3;
+
+
a3;
/
/
下一个
a1
=
v11;
LABEL_13:
if
(
+
+
v4 >
=
a1 )
/
/
数据取完,返回成功
return
1
;
}
if
( v3
+
v6
=
=
'9'
)
/
/
行结束判断,本行数据个数
+
最后的数字要为
9
{
v3
=
0
;
v5
+
=
9
;
/
/
数字转换表指向下一组
goto LABEL_13;
}
return
-
1
;
}
int
__fastcall sub_401000(
int
a1,
int
a2)
{
int
v2;
/
/
esi
int
*
v3;
/
/
edi
int
v4;
/
/
eax
int
v5;
/
/
eax
int
v6;
/
/
eax
int
v7;
/
/
eax
int
v8;
/
/
eax
int
v9;
/
/
eax
int
v10;
/
/
eax
int
v11;
/
/
eax
int
v12;
/
/
eax
int
(
*
v13)[
9
];
/
/
eax
int
v14;
/
/
edi
int
v15;
/
/
esi
int
*
v16;
/
/
ebx
int
v17;
/
/
ecx
int
v18;
/
/
edx
int
v19;
/
/
eax
int
*
v20;
/
/
eax
int
v21;
/
/
edi
int
*
v22;
/
/
ebx
int
*
v23;
/
/
ecx
int
v24;
/
/
eax
int
v25;
/
/
esi
BYTE
*
v26;
/
/
esi
int
v27;
/
/
edx
int
v28;
/
/
ebx
int
v29;
/
/
edi
int
v30;
/
/
ecx
char
*
v31;
/
/
ebx
int
*
v32;
/
/
edx
int
v33;
/
/
eax
int
v34;
/
/
ecx
int
v35;
/
/
ecx
int
v36;
/
/
eax
int
v38;
/
/
[esp
+
Ch] [ebp
-
Ch]
int
*
v39;
/
/
[esp
+
10h
] [ebp
-
8h
]
int
v40;
/
/
[esp
+
10h
] [ebp
-
8h
]
int
(
*
v41)[
9
];
/
/
[esp
+
14h
] [ebp
-
4h
]
int
*
v42;
/
/
[esp
+
14h
] [ebp
-
4h
]
int
v43;
/
/
[esp
+
14h
] [ebp
-
4h
]
v2
=
0
;
v3
=
&dword_4187C0[
0
][
1
];
do
/
/
遍历
9
*
9
的格子,一行
9
个数,为
0
就需要重输入序列中取一个填入
/
/
不过所有输入在一个队列中,只要个数不小于所需求的个数就行,所以之前转换SN到数独数据时按行读(需要行结束符)并无意义
{
if
( !
*
(v3
-
1
) )
{
v4
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
*
(v3
-
1
)
=
v4;
}
if
( !
*
v3 )
{
v5
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
*
v3
=
v5;
}
if
( !v3[
1
] )
{
v6
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
1
]
=
v6;
}
if
( !v3[
2
] )
{
v7
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
2
]
=
v7;
}
if
( !v3[
3
] )
{
v8
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
3
]
=
v8;
}
if
( !v3[
4
] )
{
v9
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
4
]
=
v9;
}
if
( !v3[
5
] )
{
v10
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
5
]
=
v10;
}
if
( !v3[
6
] )
{
v11
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
6
]
=
v11;
}
if
( !v3[
7
] )
{
v12
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
7
]
=
v12;
}
if
( v2 >
=
a2 )
break
;
v3
+
=
9
;
}
while
( (
int
)v3 < (
int
)&unk_418908 );
/
/
共
9
行
v13
=
dword_4187C0;
v14
=
0
;
v41
=
dword_4187C0;
while
(
2
)
/
/
检查每一行,不能有重复的数字
{
v15
=
1
;
v16
=
(
int
*
)v13;
do
{
v17
=
v15;
if
( v15 <
9
)
{
v18
=
*
v16;
while
( v18 )
/
/
不能为
0
,也就是没有填的空
{
v19
=
dword_4187C0[v14][v17];
if
( !v19 || v18
=
=
v19 )
/
/
比较本行中当前列之后的数不能有
0
,不能重复
break
;
/
/
验证失败,则返回
0
if
(
+
+
v17 >
=
9
)
goto LABEL_30;
}
return
0
;
}
LABEL_30:
+
+
v15;
+
+
v16;
/
/
下一列
}
while
( v15 <
10
);
/
/
共
9
列
+
+
v14;
v13
=
v41
+
1
;
v41
=
v13;
/
/
下一行
if
( (
int
)v13 < (
int
)&unk_418904 )
/
/
共
9
行
continue
;
break
;
}
v20
=
dword_4187C0[
1
];
v39
=
dword_4187C0[
1
];
while
(
2
)
/
/
检查每一列,不能有重复的数字
{
v21
=
1
;
v42
=
v20;
v22
=
v20
-
9
;
v23
=
v20;
do
{
v24
=
v21;
if
( v21 <
9
)
{
v25
=
*
v22;
while
( v25 &&
*
v23 && v25 !
=
*
v23 )
/
/
本列中当前行之后的数字不能同前面有重复
{
+
+
v24;
v23
+
=
9
;
if
( v24 >
=
9
)
{
v23
=
v42;
goto LABEL_41;
}
}
return
0
;
/
/
验证失败,则返回
0
}
LABEL_41:
+
+
v21;
v23
+
=
9
;
/
/
指向下一行
v22
+
=
9
;
v42
=
v23;
}
while
( v21 <
10
);
v20
=
v39
+
1
;
/
/
指向下一列
v39
=
v20;
if
( (
int
)v20 < (
int
)dword_4187C0[
2
] )
continue
;
break
;
}
v26
=
(BYTE
*
)malloc(
0xAu
);
/
/
分配计数数组[
0
-
9
],对小方格中的数字出现次数计数,
0
不能出现,
1
-
9
个数字必须且只能出现一次,也就是计数数组最后为[
0
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
]
if
( v26 )
{
v27
=
0
;
v28
=
0
;
v43
=
0
;
v38
=
0
;
LABEL_45:
v29
=
0
;
LABEL_46:
*
(_QWORD
*
)v26
=
0i64
;
*
((_WORD
*
)v26
+
4
)
=
0
;
if
( !__OFSUB__(v27, v27
+
3
) )
{
v30
=
3
;
/
/
小方格共
3
列
v31
=
(char
*
)dword_4187C0
+
4
*
v28
+
4
*
v29;
/
/
指向
3
*
3
小方格数据开始位置
v40
=
3
;
do
{
if
( !__OFSUB__(v29, v29
+
3
) )
{
v32
=
(
int
*
)v31;
v33
=
3
;
/
/
小方格一行
3
个数
do
{
v34
=
*
v32
+
+
;
/
/
数字做索引,对计数数组计数加一
+
+
v26[v34];
/
/
计数加
1
-
-
v33;
}
while
( v33 );
v30
=
v40;
}
v31
+
=
36
;
v40
=
-
-
v30;
}
while
( v30 );
v27
=
v43;
v28
=
v38;
}
if
( !
*
v26 )
/
/
第一个计数要为
0
, 也就是小方格中
0
不能出现
{
v35
=
0
;
v36
=
1
;
while
( (char)v26[v36] <
=
1
)
/
/
计数不能大于
1
,也就是说小方格中不能有重复的数字
{
+
+
v36;
+
+
v35;
if
( v36 >
=
10
)
/
/
本小方格中所有
1
-
9
的数字都出现且仅出现
1
次,验证通过
{
if
( v35 !
=
9
)
break
;
v29
+
=
3
;
if
( v29 <
9
)
goto LABEL_46;
v28
+
=
27
;
/
/
指向下一个小方格
v27
+
=
3
;
v43
=
v27;
v38
=
v28;
if
( v28 <
81
)
goto LABEL_45;
free(v26);
/
/
9
个
3
*
3
的小方格都没有重复的,数独验证通过,返回
1
return
1
;
}
}
}
free(v26);
}
return
0
;
/
/
验证失败,返回
0
}
int
__fastcall sub_401000(
int
a1,
int
a2)
{
int
v2;
/
/
esi
int
*
v3;
/
/
edi
int
v4;
/
/
eax
int
v5;
/
/
eax
int
v6;
/
/
eax
int
v7;
/
/
eax
int
v8;
/
/
eax
int
v9;
/
/
eax
int
v10;
/
/
eax
int
v11;
/
/
eax
int
v12;
/
/
eax
int
(
*
v13)[
9
];
/
/
eax
int
v14;
/
/
edi
int
v15;
/
/
esi
int
*
v16;
/
/
ebx
int
v17;
/
/
ecx
int
v18;
/
/
edx
int
v19;
/
/
eax
int
*
v20;
/
/
eax
int
v21;
/
/
edi
int
*
v22;
/
/
ebx
int
*
v23;
/
/
ecx
int
v24;
/
/
eax
int
v25;
/
/
esi
BYTE
*
v26;
/
/
esi
int
v27;
/
/
edx
int
v28;
/
/
ebx
int
v29;
/
/
edi
int
v30;
/
/
ecx
char
*
v31;
/
/
ebx
int
*
v32;
/
/
edx
int
v33;
/
/
eax
int
v34;
/
/
ecx
int
v35;
/
/
ecx
int
v36;
/
/
eax
int
v38;
/
/
[esp
+
Ch] [ebp
-
Ch]
int
*
v39;
/
/
[esp
+
10h
] [ebp
-
8h
]
int
v40;
/
/
[esp
+
10h
] [ebp
-
8h
]
int
(
*
v41)[
9
];
/
/
[esp
+
14h
] [ebp
-
4h
]
int
*
v42;
/
/
[esp
+
14h
] [ebp
-
4h
]
int
v43;
/
/
[esp
+
14h
] [ebp
-
4h
]
v2
=
0
;
v3
=
&dword_4187C0[
0
][
1
];
do
/
/
遍历
9
*
9
的格子,一行
9
个数,为
0
就需要重输入序列中取一个填入
/
/
不过所有输入在一个队列中,只要个数不小于所需求的个数就行,所以之前转换SN到数独数据时按行读(需要行结束符)并无意义
{
if
( !
*
(v3
-
1
) )
{
v4
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
*
(v3
-
1
)
=
v4;
}
if
( !
*
v3 )
{
v5
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
*
v3
=
v5;
}
if
( !v3[
1
] )
{
v6
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
1
]
=
v6;
}
if
( !v3[
2
] )
{
v7
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
2
]
=
v7;
}
if
( !v3[
3
] )
{
v8
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
3
]
=
v8;
}
if
( !v3[
4
] )
{
v9
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
4
]
=
v9;
}
if
( !v3[
5
] )
{
v10
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
5
]
=
v10;
}
if
( !v3[
6
] )
{
v11
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
6
]
=
v11;
}
if
( !v3[
7
] )
{
v12
=
*
(_DWORD
*
)(a1
+
4
*
v2
+
+
);
v3[
7
]
=
v12;
}
if
( v2 >
=
a2 )
break
;
v3
+
=
9
;
}
while
( (
int
)v3 < (
int
)&unk_418908 );
/
/
共
9
行
v13
=
dword_4187C0;
v14
=
0
;
v41
=
dword_4187C0;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-5-16 21:57
被AloneWolf编辑
,原因:
赞赏
他的文章
看原图
赞赏
雪币:
留言: