1
2
3
|
.text:
0000000140001324
FF
15
2E
4D
00
00
call cs:IsDebuggerPresent
.text:
000000014000132A
85
C0 test eax, eax
.text:
000000014000132C
74
05
jz short loc_140001333
|
1
2
3
4
|
.text:
0000000140001324
FF
15
2E
4D
00
00
call cs:IsDebuggerPresent
.text:
000000014000132A
|
31C0
| xor eax,eax |
.text:
000000014000132C
| EB
05
| jmp blackclient_.
140001333
|
.text:
000000014000132E
| E8
65FFFFFF
| call blackclient_.
140001298
|
|
1
2
3
4
|
.text:
0000000140001484
FF
15
DE
4B
00
00
call cs:CheckRemoteDebuggerPresent
.text:
000000014000148A
83
7D
38
00
cmp
[rbp
+
pbDebuggerPresent],
0
.text:
000000014000148E
0F
84
95
00
00
00
jz loc_140001529
.text:
0000000140001494
B9
08
00
00
00
mov ecx,
8
; Size
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
fun_scanf((__int64)
"%s"
, input_1, v64);
fun_decode(v15, v52, (__int64)
"*s>0?"
,
5
);
/
/
kctf
sub_1400026AC(&Src, v16, v52, v17, qword_140009A80);
/
/
kctf拼接到字符串前面
sub_140002BBA((__int64)v19, (__int64)&Source,
0i64
, (__int64)&v69, Size);
/
/
NtAllocateVirtualMemory
sub_140002DA9((__int64)v19);
/
/
NtWriteVirtualMemory
fun_decode(v20, v58, (__int64)
"!?>d*"
,
5
);
/
/
kernel32.dll
fun_decode(v22, v55, (__int64)
"?x)da"
, v21);
/
/
RtlFillMemory(地址,大小,值)
( (unsigned
int
)sub_140003529((__int64)&hThread) )
/
/
NtCreateThreadEx
strncpy((char
*
)&Str1, Source
+
67
,
3ui64
);
if
( !strcmp((const char
*
)&Str1,
"110"
) )
/
/
此时Str1
=
=
"110"
{
v43
=
&unk_140006590;
}
else
{
if
( strcmp((const char
*
)&Str1,
"120"
) )
/
/
不等于
120
,就退出
ExitProcess(
0
);
v43
=
&unk_140006598;
}
fun_decode(&Dest,
3
, (__int64)v43,
3
);
fun_printf((__int64)&unk_14000659C, &Dest);
/
/
这里肯定就是打印成功了
|
代码中有很多syscall,win11调试进不去,需要布置win10虚拟机调试,才能看到函数名
创建的第一个线程,每次调用RtlFillMemory拷贝1字节,跟踪发现拷贝的就是 unk_140008050 中的数据,而且看着像jmp指令:
用IDA加载能识别为代码,但是有很多错误。需要x64dbg调试shellcode。
先内存断,再执行断。shellcode运行后会将所有17替换成00,重新拿到shellcode,IDA F5就能完美识别
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
|
__int64 sub_20217()
{
__int64 (__fastcall
*
CreateToolhelp32Snapshot)(signed __int64, _QWORD);
/
/
rbx
__int64 (__fastcall
*
Process32First)(__int64,
int
*
);
/
/
r13
void (__fastcall
*
CloseHandle)(__int64);
/
/
r15
__int64 (
*
GetCurrentProcessId)(void);
/
/
r12
__int64 process;
/
/
rdi
bool
v5;
/
/
si
__int64 handle;
/
/
rax
__int64 v7;
/
/
r14
int
v9;
/
/
eax
__int64 (__fastcall
*
Process32Next_1)(__int64,
int
*
);
/
/
rbx
__int64 (__fastcall
*
OpenProcess_1)(signed __int64, _QWORD);
/
/
r13
__int64 address;
/
/
rbx
int
v13;
/
/
eax
int
*
v14;
/
/
rdx
_BYTE
*
v15;
/
/
rbx
int
*
lpBuffer;
/
/
[rsp
+
48h
] [rbp
-
2B0h
]
int
v17;
/
/
[rsp
+
58h
] [rbp
-
2A0h
]
__int64 v18;
/
/
[rsp
+
60h
] [rbp
-
298h
]
int
v19;
/
/
[rsp
+
68h
] [rbp
-
290h
]
int
v20;
/
/
[rsp
+
80h
] [rbp
-
278h
]
int
v21;
/
/
[rsp
+
88h
] [rbp
-
270h
]
__int64 Process32Next;
/
/
[rsp
+
300h
] [rbp
+
8h
]
__int64 OpenProcess;
/
/
[rsp
+
308h
] [rbp
+
10h
]
__int64 (__fastcall
*
VirtualQueryEx)(__int64, __int64,
int
*
*
, signed __int64);
/
/
[rsp
+
310h
] [rbp
+
18h
]
CreateToolhelp32Snapshot
=
(__int64 (__fastcall
*
)(signed __int64, _QWORD))sub_20563(
0xF88DDF46
);
OpenProcess
=
sub_20563(
0xFD0B55A7
);
VirtualQueryEx
=
(__int64 (__fastcall
*
)(__int64, __int64,
int
*
*
, signed __int64))sub_20563(
0x242E6FF
);
Process32First
=
(__int64 (__fastcall
*
)(__int64,
int
*
))sub_20563(
0x3F347695
);
Process32Next
=
sub_20563(
-
1813961927
);
CloseHandle
=
(void (__fastcall
*
)(__int64))sub_20563(
0x1CA655F1
);
GetCurrentProcessId
=
(__int64 (
*
)(void))sub_20563(
55981281
);
process
=
0i64
;
v20
=
0x238
;
v5
=
0
;
handle
=
CreateToolhelp32Snapshot(
2i64
,
0i64
);
v7
=
handle;
if
( handle
=
=
-
1
)
return
0xFFFFFFFFi64
;
v9
=
Process32First(handle, &v20);
Process32Next_1
=
(__int64 (__fastcall
*
)(__int64,
int
*
))Process32Next;
OpenProcess_1
=
(__int64 (__fastcall
*
)(signed __int64, _QWORD))OpenProcess;
while
( v9 )
{
if
( v21
=
=
(unsigned
int
)GetCurrentProcessId() )
{
process
=
OpenProcess_1(
0x2000000i64
,
0i64
);
if
( process )
{
address
=
0i64
;
while
(
1
)
{
do
{
if
( !VirtualQueryEx(process, address, &lpBuffer,
48i64
) )
{
Process32Next_1
=
(__int64 (__fastcall
*
)(__int64,
int
*
))Process32Next;
OpenProcess_1
=
(__int64 (__fastcall
*
)(signed __int64, _QWORD))OpenProcess;
goto LABEL_20;
}
address
=
(__int64)lpBuffer
+
v18;
}
while
( v19 !
=
4096
|| v17 !
=
64
);
v13
=
GetCurrentProcessId();
v14
=
lpBuffer;
if
( v21
=
=
v13 )
v5
=
sub_2062F(
*
lpBuffer);
if
( v5 )
break
;
*
(_BYTE
*
)v14
=
'm'
;
/
/
第一层判断,kctf头
*
((_BYTE
*
)v14
+
1
)
=
'j'
;
*
((_BYTE
*
)v14
+
2
)
=
')'
;
*
((_BYTE
*
)v14
+
3
)
=
0
;
*
((_BYTE
*
)v14
+
67
)
=
'1'
;
*
((_BYTE
*
)v14
+
68
)
=
'2'
;
*
((_BYTE
*
)v14
+
69
)
=
'0'
;
}
v15
=
v14
+
1
;
if
( sub_20AA3((__int64)(v14
+
1
)) )
/
/
所有判断,都在这里呗 传入kctf后面的字符串
{
*
(v15
-
4
)
=
'i'
;
/
/
正确答案!!!!!!
*
(v15
-
3
)
=
'o'
;
*
(v15
-
2
)
=
' '
;
*
(v15
-
1
)
=
0
;
v15[
63
]
=
'1'
;
v15[
64
]
=
'1'
;
}
else
{
*
(v15
-
4
)
=
'm'
;
*
(v15
-
3
)
=
'j'
;
*
(v15
-
2
)
=
')'
;
*
(v15
-
1
)
=
0
;
v15[
63
]
=
'1'
;
v15[
64
]
=
'2'
;
}
v15[
65
]
=
'0'
;
break
;
}
}
LABEL_20:
v9
=
Process32Next_1(v7, &v20);
}
CloseHandle(v7);
return
((__int64 (__fastcall
*
)(__int64))CloseHandle)(process);
}
|
然后就是反复调试,发现字符串分为3块:
3201382652D139C0E22132DF1BC2212EA0991650A229B36436823D0B13D51E6 //前缀,3位一个16进制数
input //必须是3的倍数,后续分析长度必须是180
677116575313142309154604431859253431473963507533496829080645035455771774602058076430276921790210013736267644383505517280 //后缀 2位一个10进制数
核心函数是2063B,要求input每3位,是一个十六进制
比如 input[0]="112",0x112=274,十位*9+个位=67,要跟后缀[0]相等
十位和个位就确定了。
同时十位和个位要作为下标,充当数独的坐标。百位就是数独的内容。
数独一共是9*9个格子,其中前缀是21个数,input是60个数。所以先转换已有的21个数,更新到棋盘
再求解出答案:
编程反算数独下标,
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
|
74
,[
7
][
4
]的数字
=
2
,
274
=
0x112
,
input
[
0
]
=
"112"
78
,[
7
][
8
]的数字
=
7
,
778
=
0x30a
,
input
[
2
]
=
"30A"
,有限制是大写字母
17
,以此类推,
63
58
14
15
25
10
16
51
04
47
20
65
27
37
34
52
43
70
55
83
36
54
75
32
08
06
50
03
60
61
85
18
82
66
22
64
07
71
33
30
76
23
87
02
11
01
41
40
28
84
48
42
38
05
56
80
88
|
更多【KCTF2023 第六题 至暗时刻】相关视频教程:www.yxfzedu.com