【CTF对抗- KCTF 2023 第十二题 wp - 98k】此文章归类为:CTF对抗。
程序中的一些关键函数加了混淆,直接把 0x49A0E8 处 patch 为 double 类型的 100.0 ,再声明其类型为 const double 这样 ida F5 就能直接去除无关的逻辑。
题目实现了一个虚拟机,程序启动后会先解密得到虚拟机执行的命令,之后来到函数 0x460D0E 执行:
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
|
char
__thiscall global_vtable_2_func_18(C *
this
)
{
C *v1;
// ebx
int
_pc;
// esi
B *ins_start;
// edi MAPDST
char
*v6;
// esi
_DWORD *v7;
// eax
const
char
*v8;
// ecx
unsigned
int
v9;
// edx
unsigned
int
v10;
// edi
_BYTE *v11;
// ebx
char
v12;
// al
int
v14;
// [esp+8h] [ebp-6Ch]
char
v16[88];
// [esp+1Ch] [ebp-58h] BYREF
v1 =
this
;
if
( v1->instructions.start == v1->instructions.finish )
return
0;
this
->pc = 0;
ins_start =
this
->instructions.start;
if
( v1->instructions.finish - ins_start > 0 )
{
_pc = 0;
ins_start = v1->instructions.start;
do
{
if
( _pc == -1 )
{
v1->pc = 0;
_pc = 0;
}
if
( !(*((unsigned
__int8
(__thiscall **)(C *, B *))v1->vtable + 15))(v1, &ins_start[_pc]) )
{
// show error message
}
_pc = v1->pc + 1;
v1->pc = _pc;
ins_start = v1->instructions.start;
}
while
( _pc < v1->instructions.finish - ins_start );
}
return
1;
}
|
v1->vtable + 15
就是虚表中的第 15 项,地址为 0x4525EB 。
相关数据结构:
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
|
struct
A
{
int
field_0;
std::string s0;
int
i1;
int
i2;
int
i3;
std::string s4;
std::string s5;
std::string s6;
};
struct
std::vector$A$
{
A *start;
A *finish;
A *end_of_storage;
};
struct
B
{
int
is_label;
int
field_4;
std::string s1;
std::string s2;
std::vector$A$ vector;
};
struct
std::vector$B$
{
B *start;
B *finish;
B *end_of_storage;
};
struct
RBTree
// std::map<std::string, std::string>
{
struct
RBTree *left;
struct
RBTree *parent;
struct
RBTree *right;
char
color;
char
isnil;
char
padding[2];
std::string key;
std::string value;
};
struct
C
{
int
**vtable;
RBTree *tree;
int
node_count;
std::vector$B$ instructions;
int
pc;
char
valid_data;
};
|
将解密之后的指令所在的内存 dump 出来,写脚本恢复原始指令:
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
|
def
vm(ins):
opcode
=
int
(ins[
2
])
operands
=
ins[
3
]
if
opcode
=
=
0x21344D4938CE0640
:
print
(
'system({0})'
.
format
(
repr
(operands[
0
][
0
].decode())))
elif
opcode
=
=
0x3820EA1739C3E154
:
print
(
'map[{0}] = hex(vigenere(map[{0}], "恭喜发财".encode("gbk")))'
.
format
(
repr
(operands[
0
][
4
])))
elif
opcode
=
=
0x4B0134D06B40680
:
print
(
'if (len(map[{0}]) {1} {2}) goto {3}'
.
format
(
repr
(operands[
0
][
4
]), operands[
1
][
5
].decode(), operands[
2
][
1
], operands[
3
][
6
].decode()))
elif
opcode
=
=
0x5304FD305CA8C22A
:
print
(
'sleep({})'
.
format
(operands[
0
][
1
]))
elif
opcode
=
=
0x5AC009C0F14B76E8
:
print
(
'map[{0}] = input()'
.
format
(
repr
(operands[
0
][
4
])))
elif
opcode
=
=
0x6975C7A3C07CD226
:
print
(
'map[{0}] += {1}'
.
format
(
repr
(operands[
0
][
4
]),
repr
(operands[
1
][
0
])))
elif
opcode
=
=
0x7929CBF0A1496FB0
:
print
(
'map[{0}] = base64encode(map[{0}])'
.
format
(
repr
(operands[
0
][
4
])))
elif
opcode
=
=
0x885F75A1461ECEBB
:
print
(
'if (map[{0}] == map[{1}]) goto {2}'
.
format
(
repr
(operands[
0
][
4
]),
repr
(operands[
1
][
4
]), operands[
2
][
6
].decode()))
elif
opcode
=
=
0x8DB9D83D80004137
:
print
(
'print({})'
.
format
(
repr
(operands[
0
][
0
].replace(b
'\\n'
, b
'\n'
).decode(
'gbk'
))))
elif
opcode
=
=
0xA43CBF9D015186F1
:
if
len
(operands): code
=
operands[
0
][
1
]
else
: code
=
0
print
(
'exit({0})'
.
format
(code))
elif
opcode
=
=
0xAA9C8E70F01F8D61
:
print
(
'map[{0}] = map[{1}]'
.
format
(
repr
(operands[
0
][
4
]),
repr
(operands[
1
][
4
])))
elif
opcode
=
=
0xDDEEFF2200112233
:
print
(
'nop'
)
elif
opcode
=
=
0xE56D33B21C50A892
:
print
(
'goto {0}'
.
format
(operands[
0
][
6
].decode()))
elif
opcode
=
=
0xF4CC06C2E34200F0
:
print
(
'nop 0'
)
elif
opcode
=
=
0xF4CC06F2E3420459
:
print
(
'nop 1'
)
elif
opcode
=
=
0xFD1D1DFB19850CA1
:
print
(
'map[{0}] = md5(map[{0}])'
.
format
(
repr
(operands[
0
][
4
])))
else
:
assert
False
,
hex
(opcode)
mem_offset
=
0xea0000
mem_size
=
0x20000
mem_dump
=
open
(
'MEM_00EA0000_00020000.mem'
,
'rb'
).read()
ins_start
=
0xeb4b00
ins_end
=
0xeb62a4
def
read_dword(addr):
assert
mem_offset <
=
addr < mem_offset
+
mem_size
return
int
.from_bytes(mem_dump[addr
-
mem_offset: addr
-
mem_offset
+
4
],
'little'
)
def
read_data(addr, size):
assert
mem_offset <
=
addr < mem_offset
+
mem_size
-
size
return
mem_dump[addr
-
mem_offset: addr
-
mem_offset
+
size]
def
read_str(addr):
size
=
read_dword(addr
+
0x10
)
cap
=
read_dword(addr
+
0x14
)
if
cap >
0x10
: addr
=
read_dword(addr)
return
read_data(addr, size)
def
read_A(addr):
return
(read_str(addr
+
4
), read_dword(addr
+
0x1c
), read_dword(addr
+
0x20
), read_dword(addr
+
0x24
), read_str(addr
+
0x28
), read_str(addr
+
0x40
), read_str(addr
+
0x58
))
def
read_B(addr):
B
=
(read_dword(addr), read_dword(addr
+
4
), read_str(addr
+
0x20
), [])
A_start
=
read_dword(addr
+
0x38
)
A_end
=
read_dword(addr
+
0x3c
)
for
addr
in
range
(A_start, A_end,
0x70
):
B[
-
1
].append(read_A(addr))
return
B
ins
=
[]
for
addr
in
range
(ins_start, ins_end,
0x44
):
ins.append(read_B(addr))
for
i
in
range
(
len
(ins)):
t
=
ins[i]
if
t[
0
]:
print
(t[
3
][
0
][
6
].decode()
+
':'
)
else
:
# print(i, end=': ')
print
(
' '
, end
=
'')
vm(ins[i])
|
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
|
sleep(
25
)
print
(
' \n'
)
sleep(
25
)
print
(
' _oo0oo_ \n'
)
sleep(
25
)
print
(
' o8888888o \n'
)
sleep(
25
)
print
(
' 88" . "88 \n'
)
sleep(
25
)
print
(
' (| -_- |) \n'
)
sleep(
25
)
print
(
' 0\\ = /0 \n'
)
sleep(
25
)
print
(
" ___/`---'\\___ \n"
)
sleep(
25
)
print
(
" .'\\.\\| |/./'. \n"
)
sleep(
25
)
print
(
' / \\.\\|| : ||/./ \\ \n'
)
sleep(
25
)
print
(
' / _||||| -:- |||||- \\ \n'
)
sleep(
25
)
print
(
' | | \\.\\.\\ - /././ | | \n'
)
sleep(
25
)
print
(
" | \\_| ''\\---/'' |_/ | \n"
)
sleep(
25
)
print
(
" \\ .-\\__ '-' ___/-. / \n"
)
sleep(
25
)
print
(
" ___'. .' /--.--\\ `. .'___ \n"
)
sleep(
25
)
print
(
' ."" \'< `.___\\_<|>_/___.\' >\' "". \n'
)
sleep(
25
)
print
(
' | | : `- \\`.;`\\ _ /`;.`/ - ` : | | \n'
)
sleep(
25
)
print
(
' \\ \\ `_. \\_ __\\ /__ _/ .-` / / \n'
)
sleep(
25
)
print
(
"=====`-.____`.___ \\_____/___.-`___.-'===== \n"
)
sleep(
25
)
print
(
" `=---=' \n"
)
sleep(
25
)
print
(
' \n'
)
sleep(
25
)
print
(
' 佛祖保佑 永无BUG \n'
)
sleep(
25
)
print
(
' 论坛 KCTF2023年度赛 \n'
)
sleep(
25
)
print
(
' 出题战队:中午吃什么 \n'
)
sleep(
25
)
print
(
' \n'
)
sleep(
25
)
print
(
' \n'
)
nop
sleep(
25
)
print
(
'请输入用户名:\n--> '
)
map
[b
'2569430338759937617'
]
=
input
()
nop
sleep(
25
)
print
(
'请输入序列号:\n--> '
)
map
[b
'1509181994979340817'
]
=
input
()
nop
nop
0
nop
1
if
(
len
(
map
[b
'1509181994979340817'
]) !
=
32
) goto
10547232137042693405
nop
map
[b
'18097274335226857185'
]
=
map
[b
'2569430338759937617'
]
map
[b
'18097274335226857185'
]
+
=
b
'KCTF2023'
map
[b
'18097274335226857185'
]
=
hex
(vigenere(
map
[b
'18097274335226857185'
],
"恭喜发财"
.encode(
"gbk"
)))
map
[b
'18097274335226857185'
]
=
base64encode(
map
[b
'18097274335226857185'
])
map
[b
'18097274335226857185'
]
=
md5(
map
[b
'18097274335226857185'
])
nop
if
(
map
[b
'18097274335226857185'
]
=
=
map
[b
'1509181994979340817'
]) goto
15317636321340901566
goto
15315557398280875957
nop
10547232137042693405
:
sleep(
25
)
print
(
'error\n\n\n'
)
system(
'pause'
)
exit(
0
)
nop
15317636321340901566
:
sleep(
25
)
print
(
'Success GoodJob!\n\n\n'
)
system(
'pause'
)
exit(
0
)
nop
15315557398280875957
:
sleep(
25
)
print
(
'error\n\n\n'
)
system(
'pause'
)
exit(
0
)
|
求解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#!/usr/bin/env python3
from
base64
import
b64encode
from
hashlib
import
md5
def
vigenere(x, y):
return
bytes(x[i] ^ y[i
%
len
(y)]
for
i
in
range
(
len
(x)))
def
solve(username):
return
md5(b64encode(vigenere(username
+
b
'KCTF2023'
,
"恭喜发财"
.encode(
"gbk"
)).
hex
().upper().encode())).hexdigest()
'''
User-Name : 4070382B95A4F0ED
Serial-Number : 47a62c6eb8a72031a27b89abc3d976f7
'''
assert
solve(b
'4070382B95A4F0ED'
)
=
=
'47a62c6eb8a72031a27b89abc3d976f7'
print
(solve(b
'KCTF'
))
# 35090e1336f1d1e872ba798256db1bfb
|
更多【CTF对抗- KCTF 2023 第十二题 wp - 98k】相关视频教程:www.yxfzedu.com