KCTF 总决赛第六题

西部乐园 WP

第六题 三道八佛

题目信息:Windows

题目来源:2019 看雪CTF总决赛

题目是多层SMC完成的对于代码的加密,使用OD加载程序以后发现跑起来非常的卡。

image-20200111011819888

由于SMC的代码量很大,所以在OD中不太好用,我选择了x32dbg。

image-20200111012429056

由于采用了多层的smc,所以在ida中是无法直接分析算法的。

程序开头进行两个 VirtualProtect 进行修改.修改属性为可读写执行. 下面会不断的进行代码重定位.并且操作 FS:[4] FS:[8] 等来进行保存用户输入的UserName 以及 序列号

img

image-20200111013459617

动态跑起来,可以发现最初输入的KCTF 被填充了。

image-20200111013823999

下面的call eax 就是进行切换堆栈,并且跳入下一层smc 中。并且开始抹除原始代码。

image-20200111013943021

$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代码如下。

image-20200111014720242

代码比较清晰了,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()

最后成功跑出结果。

image-20200111014829398


   转载规则


《KCTF 总决赛第六题》 Hook 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
tiny 实验报告 tiny 实验报告
实验报告分析该语言的词法和语法规则,分析其编译器的运行命令和代码组成结构词法规则下面是LEX中的变量声明 定义段可以包含任意的C语言文件,符号说明,其代码会被直接拷贝到生成的扫描器代码文件中 声明示例如下 %{ … %} digit
2020-01-11
下一篇 
hookme hookme
新的博客开始啦~认真整理东西!!! 冲!
2019-12-26
  目录