前几天,一位学弟发了道很基础的vm题目给我,我自己想用多种思路来解,中间碰壁多次,于是想着写一篇博客分享下碰壁过程以及关于vm题型我自己的一些解法
程序大多会有自己一套循环的执行程序流
大致长相为
其中opcode一般情况是由操作数和操作组成的一大串数据(通常由成百上千个byte组成)
说白了就是其中的加密过程很多,我们需要模拟执行流
关键定位:虚拟指令分发器(opcode)
加密逻辑:找到他的循环加密
模拟重建:模拟执行流进行爆破或者解密
将整个执行流程保存下来,倒着用enc去回到flag
模拟加密过程,使用Z3约束求解来得到flag
因为单字节或者双字节的缘故,所有可能也不过那么几样,我们通过一些手段可以实现爆破程序得到flag
2024年强网杯就有一道vm,对我来说很难
找不到opcode和中间的执行流
这种题我目前的想法也只会无脑的爆破程序,不出也不会了,硬看经常大脑卡死
(大佬轻松手撕)
解题前放两位大佬关于vm的博客,我也是跟着学的~
upsw1ng佬关于vm的博客
swdd佬关于利用frida去插桩爆破的博客
因为前面两种方法都要去提取opcode,所以放一起了
我们以isctf2022年的一道很基础的vm来进行讲解
(题目已经上传了)

很纯粹的vm,不带任何杂质
一眼看见了程序加密逻辑

同时可以看见i是我们的opcode,i是由v8赋值而来,我们再看一眼v8

双击进去

基本上是opcode无疑
这里我们可以选择第一个变量,shift+8提取opcode
一共24个变量,一个变量16 byte

这里多提取一点以防万一
当然这里也可以使用idapython来提取,我就不过多赘述了
贴个脚本
提取了opcode我们再去复现执行流
这里有个坑了,我贴上最后的代码片段大家可以对比下

可以看见很多地方不一样,至于原理我就不是很懂了,有佬知道可以解答下吗~
具体解密流程建议大家去看汇编代码

现在opcode和执行流程有了,就是我们的求解方法了
这里衍生出了两张,一种是直接手撕逆回去,因为+-^都是可以逆的
先贴exp
这里因为异或优先级的原因,所以我选择把opcode存起来,然后从后往前进行解密

中间打印的那些,只是为了我当时方便去调试
这里有两个异曲同工的解法
首先推荐的是第一种
这里有个巨大的坑,卡了我十个小时,因为约束求解多项式的原因,优先级问题,我们在用flag去进行encode的时候,必须要创建一个副本,不然会因为符号变量原因什么的,导致中间那个&0xff和^的优先级会混乱
像下面这样

所以一定要encoded_flag = encode(flag, opcode)
flag副本去solver
当然你也可以一个一个去打印出来,这样方便你去调试,这道题的wp就是这样做的,我也不水字数了,大家可以自己去看看
这里不会的师傅一定去看看上面给大家推荐的swdd的博客,我就直接贴脚本了
frida代码
报告大帅!解题完毕!

while
(i<
len
(opcode)):
if
(opcode[i]
=
=
0x00
):
i
+
=
1
elif
(opcode[i]
=
=
0x01
):
i
+
=
2
elif
(opcode[i]
=
=
0x02
):
i
+
=
3
while
(i<
len
(opcode)):
if
(opcode[i]
=
=
0x00
):
i
+
=
1
elif
(opcode[i]
=
=
0x01
):
i
+
=
2
elif
(opcode[i]
=
=
0x02
):
i
+
=
3
import
idaapi
import
idc
start_address
=
0x7A31D0
data_length
=
16
*
25
with
open
(
"E:\\CTF\\code\\python\\CTF\\dumpopcode.txt"
,
"a"
) as f:
for
i
in
range
(
0
,data_length):
f.write(
hex
(idc.get_wide_byte(start_address
+
i)))
f.write(
"\n"
)
import
idaapi
import
idc
start_address
=
0x7A31D0
data_length
=
16
*
25
with
open
(
"E:\\CTF\\code\\python\\CTF\\dumpopcode.txt"
,
"a"
) as f:
for
i
in
range
(
0
,data_length):
f.write(
hex
(idc.get_wide_byte(start_address
+
i)))
f.write(
"\n"
)
def
encode(flag, opcode):
encoded_flag
=
flag.copy()
i
=
1
while
i <
len
(opcode):
op_type
=
opcode[i
-
1
]
target
=
opcode[i]
value
=
opcode[i
+
1
]
if
op_type
=
=
1
:
writefile(f
"flag[{target}] = flag[{target}] + {hex(value)};"
)
i
+
=
3
elif
op_type
=
=
4
:
writefile(f
"flag[{target}] = flag[{target}] - {hex(value)};"
)
i
+
=
3
elif
op_type
=
=
5
:
writefile(f
"flag[{target}] = flag[{target}] ^ {hex(value)};"
)
i
+
=
3
else
:
break
return
encoded_flag
def
encode(flag, opcode):
encoded_flag
=
flag.copy()
i
=
1
while
i <
len
(opcode):
op_type
=
opcode[i
-
1
]
target
=
opcode[i]
value
=
opcode[i
+
1
]
if
op_type
=
=
1
:
writefile(f
"flag[{target}] = flag[{target}] + {hex(value)};"
)
i
+
=
3
elif
op_type
=
=
4
:
writefile(f
"flag[{target}] = flag[{target}] - {hex(value)};"
)
i
+
=
3
elif
op_type
=
=
5
:
writefile(f
"flag[{target}] = flag[{target}] ^ {hex(value)};"
)
i
+
=
3
else
:
break
return
encoded_flag
import
z3
def
getfile(file_path):
opcode
=
[]
with
open
(file_path,
'r'
) as
file
:
for
line
in
file
:
opcode.append(
int
(line.strip(),
16
))
return
opcode
def
enopcode(opcode):
read_opcode
=
[]
i
=
1
while
i<
len
(opcode):
while
True
:
while
opcode[i
-
1
]
=
=
1
:
read_opcode.append(opcode[i])
read_opcode.append(
1
)
read_opcode.append(opcode[i
+
1
])
i
+
=
3
if
opcode[i
-
1
] !
=
4
:
break
read_opcode.append(opcode[i])
read_opcode.append(
2
)
read_opcode.append(opcode[i
+
1
])
i
+
=
3
if
opcode[i
-
1
]
=
=
5
:
read_opcode.append(opcode[i])
read_opcode.append(
3
)
read_opcode.append(opcode[i
+
1
])
i
+
=
3
else
:
break
return
read_opcode
def
deopcode(value,opcode,number):
for
i
in
range
(
len
(opcode)
-
3
,
-
1
,
-
3
):
if
opcode[i]
=
=
number:
if
opcode[i
+
1
]
=
=
1
:
value
-
=
opcode[i
+
2
]
print
(f
'flag[{number}]-={hex(opcode[i+2])}'
)
elif
opcode[i
+
1
]
=
=
2
:
value
+
=
opcode[i
+
2
]
print
(f
'flag[{number}]+={hex(opcode[i+2])}'
)
elif
opcode[i
+
1
]
=
=
3
:
value^
=
opcode[i
+
2
]
print
(f
'flag[{number}]^={hex(opcode[i+2])}'
)
return
value&
0xff
if
__name__
=
=
'__main__'
:
oriopcode
=
getfile(
"E:\\CTF\\code\\python\\CTF\\dumpopcode.txt"
)
flag_enc
=
[
0x65
,
0xE2
,
0x57
,
0x60
,
0xCE
,
0x1E
,
0xE1
,
0x5C
,
0x4B
,
0x4B
,
0x23
,
0x6D
,
0x8C
,
0xC2
,
0xBC
,
0x58
,
0x84
,
0x92
,
0x7E
,
0x8C
,
0x43
,
0xDB
,
0x15
,
0x71
,
0x97
,
0x4A
,
0xE3
,
0xC4
,
0x1F
,
0x7C
,
0xC2
,
0xFD
]
opcode
=
enopcode(oriopcode)
flag
=
[]
for
i
in
range
(
0
,
len
(flag_enc)):
flag.append(deopcode(flag_enc[i],opcode,i))
print
(''.join(
chr
(flag[i])
for
i
in
range
(
len
(flag))))
import
z3
def
getfile(file_path):
opcode
=
[]
with
open
(file_path,
'r'
) as
file
:
for
line
in
file
:
opcode.append(
int
(line.strip(),
16
))
return
opcode
def
enopcode(opcode):
read_opcode
=
[]
i
=
1
while
i<
len
(opcode):
while
True
:
while
opcode[i
-
1
]
=
=
1
:
read_opcode.append(opcode[i])
read_opcode.append(
1
)
read_opcode.append(opcode[i
+
1
])
i
+
=
3
if
opcode[i
-
1
] !
=
4
:
break
read_opcode.append(opcode[i])
read_opcode.append(
2
)
read_opcode.append(opcode[i
+
1
])
i
+
=
3
if
opcode[i
-
1
]
=
=
5
:
read_opcode.append(opcode[i])
read_opcode.append(
3
)
read_opcode.append(opcode[i
+
1
])
i
+
=
3
else
:
break
return
read_opcode
def
deopcode(value,opcode,number):
for
i
in
range
(
len
(opcode)
-
3
,
-
1
,
-
3
):
if
opcode[i]
=
=
number:
if
opcode[i
+
1
]
=
=
1
:
value
-
=
opcode[i
+
2
]
print
(f
'flag[{number}]-={hex(opcode[i+2])}'
)
elif
opcode[i
+
1
]
=
=
2
:
value
+
=
opcode[i
+
2
]
print
(f
'flag[{number}]+={hex(opcode[i+2])}'
)
elif
opcode[i
+
1
]
=
=
3
:
value^
=
opcode[i
+
2
]
print
(f
'flag[{number}]^={hex(opcode[i+2])}'
)
return
value&
0xff
if
__name__
=
=
'__main__'
:
oriopcode
=
getfile(
"E:\\CTF\\code\\python\\CTF\\dumpopcode.txt"
)
flag_enc
=
[
0x65
,
0xE2
,
0x57
,
0x60
,
0xCE
,
0x1E
,
0xE1
,
0x5C
,
0x4B
,
0x4B
,
0x23
,
0x6D
,
0x8C
,
0xC2
,
0xBC
,
0x58
,
0x84
,
0x92
,
0x7E
,
0x8C
,
0x43
,
0xDB
,
0x15
,
0x71
,
0x97
,
0x4A
,
0xE3
,
0xC4
,
0x1F
,
0x7C
,
0xC2
,
0xFD
]
opcode
=
enopcode(oriopcode)
flag
=
[]
for
i
in
range
(
0
,
len
(flag_enc)):
flag.append(deopcode(flag_enc[i],opcode,i))
print
(''.join(
chr
(flag[i])
for
i
in
range
(
len
(flag))))
import
z3
def
getfile(file_path):
opcode
=
[]
with
open
(file_path,
'r'
) as
file
:
for
line
in
file
:
opcode.append(
int
(line.strip(),
16
))
return
opcode
def
encode(flag, opcode):
encoded_flag
=
flag.copy()
i
=
1
while
i <
len
(opcode):
op_type
=
opcode[i
-
1
]
target
=
opcode[i]
value
=
opcode[i
+
1
]
if
op_type
=
=
1
:
encoded_flag[target]
=
(encoded_flag[target]
+
value) &
0xFF
print
(f
"flag[{target}] += {hex(value)}"
)
i
+
=
3
elif
op_type
=
=
4
:
encoded_flag[target]
=
(encoded_flag[target]
-
value) &
0xFF
print
(f
"flag[{target}] -= {hex(value)}"
)
i
+
=
3
elif
op_type
=
=
5
:
encoded_flag[target]
=
(encoded_flag[target] ^ value) &
0xFF
print
(f
"flag[{target}] ^= {hex(value)}"
)
i
+
=
3
else
:
break
return
encoded_flag
if
__name__
=
=
'__main__'
:
opcode
=
getfile(
"E:\\CTF\\code\\python\\CTF\\dumpopcode.txt"
)
flag_size
=
32
flag
=
[z3.BitVec(f
'flag[{i}]'
,
8
)
for
i
in
range
(flag_size)]
flag_enc
=
[
0x65
,
0xE2
,
0x57
,
0x60
,
0xCE
,
0x1E
,
0xE1
,
0x5C
,
0x4B
,
0x4B
,
0x23
,
0x6D
,
0x8C
,
0xC2
,
0xBC
,
0x58
,
0x84
,
0x92
,
0x7E
,
0x8C
,
0x43
,
0xDB
,
0x15
,
0x71
,
0x97
,
0x4A
,
0xE3
,
0xC4
,
0x1F
,
0x7C
,
0xC2
,
0xFD
]
solver
=
z3.Solver()
encoded_flag
=
encode(flag, opcode)
for
j
in
range
(
len
(flag_enc)):
solver.add(encoded_flag[j]
=
=
flag_enc[j])
for
i
in
solver.assertions():
print
(i)
if
solver.check()
=
=
z3.sat:
model
=
solver.model()
result_flag
=
[model.evaluate(flag[i]).as_long()
for
i
in
range
(flag_size)]
print
(result_flag)
else
:
print
(
"No solution found."
)
import
z3
def
getfile(file_path):
opcode
=
[]
with
open
(file_path,
'r'
) as
file
:
for
line
in
file
:
opcode.append(
int
(line.strip(),
16
))
return
opcode
def
encode(flag, opcode):
encoded_flag
=
flag.copy()
i
=
1
while
i <
len
(opcode):
op_type
=
opcode[i
-
1
]
target
=
opcode[i]
value
=
opcode[i
+
1
]
if
op_type
=
=
1
:
encoded_flag[target]
=
(encoded_flag[target]
+
value) &
0xFF
print
(f
"flag[{target}] += {hex(value)}"
)
i
+
=
3
elif
op_type
=
=
4
:
encoded_flag[target]
=
(encoded_flag[target]
-
value) &
0xFF
print
(f
"flag[{target}] -= {hex(value)}"
)
i
+
=
3
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2025-3-3 22:41
被namename123编辑
,原因: 有个没有加载出来的图片