西部乐园 WP
第六题 三道八佛
题目信息:Windows
题目来源:2019 看雪CTF总决赛
题目是多层SMC完成的对于代码的加密,使用OD加载程序以后发现跑起来非常的卡。
由于SMC的代码量很大,所以在OD中不太好用,我选择了x32dbg。
由于采用了多层的smc,所以在ida中是无法直接分析算法的。
程序开头进行两个 VirtualProtect 进行修改.修改属性为可读写执行. 下面会不断的进行代码重定位.并且操作 FS:[4] FS:[8] 等来进行保存用户输入的UserName 以及 序列号
动态跑起来,可以发现最初输入的KCTF 被填充了。
下面的call eax 就是进行切换堆栈,并且跳入下一层smc 中。并且开始抹除原始代码。
$calleax = 0
$num = 0
$jnzaddr = 0
$f = 0
$prevaddr = 0
$retaddr=0
$hasj=0
bph 004014DF
run
//input the username and password
bphc
start:
find cip,FFD0,0x200
cmp $result,0
je over
mov $calleax,$result
bph $calleax
run
bphc
inc $num
StepInto
StepInto
StepOver
mov $jnzaddr,cip
find_jnz:
find cip,8BE55DC3,0x220
cmp $result,0
je nobp
mov $retaddr,$result
bph $retaddr+3
run
bphc
StepInto
StepInto
inc $num
mov $jnzaddr,cip
nobp:
find jnzaddr,75??,0x200
cmp $result,0
jnz isjnz
jnzs:
find cip,0F85????????,0x200
cmp $result,0
jnz isjnzs
jmp nofound
runtojnz:
mov f,0
bph $jnzaddr+2
mov $hasj,1
jmp jnzs
runtojnzs:
mov f,0
mov $jnzaddr,$result
bph $jnzaddr+6
run
bphc
mov $hasj,0
nofound:
cmp $hasj,1
jnz nojump
mov $hasj,0
run
bphc
nojump:
jmp start
isjnz:
mov $jnzaddr,$result
$prevaddr = dis.prev($jnzaddr)
$prevaddr = $prevaddr + dis.len($prevaddr)
cmp $prevaddr,$jnzaddr
je runtojnz
add $jnzaddr,2
jmp find_jnz
isjnzs:
mov $jnzaddr,$result
$prevaddr = dis.prev($jnzaddr)
$prevaddr = $prevaddr + dis.len($prevaddr)
cmp $prevaddr,$jnzaddr
je runtojnzs
cmp $hasj,1
jnz noj
jmp nofound
noj:
add $jnzaddr,2
jmp find_jnz
over:
cmp f,0
jnz end
mov f,1
jnzaddr=cip
jmp find_jnz
end:
log decryptnum:{$num}
msg "over"
通过比对进入smc时的特征,使用x32dbg脚本,跑完发现有1403层smc,用脚本跑到最后一层,发现下面的代码是完整的函数,代码很长,还有很多重定位和混淆,故先全部提取到code文件并修正一下esi。
用IDA分析code文件,先修改基址为0x1A1857E,F5代码如下。
代码比较清晰了,serial经过计算,跟用户名比较,如果相同则成功。
注意KCTF用户名不足16位,后面的填充数据也是要比较的。也就是说实际上比较的是
4B 43 54 46 00 1A 19 18 17 16 15 14 13 12 11 10 00
因为代码还是有点看不太懂的,所以提取反编译以后的代码,放到VS中重新编译成exe以后使用angr跑出来flag。 angr 的python代码如下
# coding=utf-8
import angr
import claripy
import base64
def main():
load_option = {}
b = angr.Project("./ConsoleApplication21.exe", load_options=load_option)
state = b.factory.blank_state(addr=0x401040)
concrete_addr = 0x404378
flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(30)]
flag = claripy.Concat(*flag_chars)
state.memory.store(concrete_addr, flag)
sm = b.factory.simulation_manager(state)
print sm.explore(find=0x4013A3)
found = sm.found[0]
temp = (found.posix.dumps(0))
print temp
solution = found.solver.eval(flag, cast_to=str)
s = []
for i in solution:
s.append(ord(i))
print s
print solution.encode("hex").upper()
print(solution)
print flag, found
if __name__ == '__main__':
main()
最后成功跑出结果。