推荐一下hackme.inndy,上边有一些很好的针对新手的题目,但网上能搜到的Writeup很少,因此开了这篇博文记录一下部分目前解出的题目(主要是pwn和re),以后会跟着解题进度的推进逐步更新,同时遵循inndy师傅的规矩,只放思路,不放flag。

zwhubuntu师傅的部分题解:http://wxzwhubuntu.club:8088/index.php/2017/06/14/inndy/

聂师傅的部分题解: http://blog.csdn.net/niexinming

如果看了解题思路仍有问题,可以在我的github找到完整的脚本:pwnreverse,欢迎star和follow

pwn

0x00 catflag

送分题,nc连接,cat flag即可

0x01 homework

根据题目提示,是数组越界的漏洞,第一次遇到这种漏洞觉得利用方式很巧妙。通过搜索字符串发现了get shell的函数call_me_maybe, run_program函数中定义了一个大小为10的数组,漏洞出现在edit number的选项里,当我们输入的index大于10时,程序是不会报错的,而是会继续朝着高地址edit数据,因此只需要edit run_program栈中的retcall_me_maybe, 这样当我们退出run_program函数时,程序就会返回到call_me_maybe函数来get_shell,原理图如下:

有两点需要注意:

  1. 因为edit number中修改的数据是通过**%d**输入的,因此需要以整形的方式输入**call_me_maybe**函数的地址
  2. 修改ret的地址之后,还需要再输入一次0来退出run_program的循环来出发ret

payload:

0x02 ROP

刚看这道题的时候觉得无从下手,程序中既没有可以利用的函数,通过file命令查看是静态链接的也不能利用libc中的函数。后来注意到下一题提到了ROPgadget这个工具,才想到可以直接利用ROPgadget直接拼凑出ROP链,如下:

这样就只需要我们通过缓冲区溢出的漏洞返回到ROPgadget构造的ropchain就可以了

payload:

0x03 ROP2

本来根据提示以为这道题需要自己拼凑出ropchain,但后来发现这一题中存在syscall这个可以实现系统调用的函数,如syscall(4, 1, &v4, 42)即相当于write(1, &v4, 42)syscall(3, 0, &v1, 1024)即相当于read(0, &v1, 1024),syscall的第一个参数是系统函数的系统调用号,之后的参数依次为对应函数的参数,32位的系统调用号定义在**/usr/include/x86_64-linux-gnu/asm/unistd_32.h**中,可以看到execve的调用号为11,因此如果我们构造**syscall(11, “/bin/sh”, 0, 0)**就相当于执行了**execve("/bin/sh”, 0, 0)**即可get shell

介绍一个小trick,如下图,可以看出v4是提示字符串的开头,但因为IDA没有正确识别变量的类型,把提示字符串分成了v4~v15多个变量,v4的地址为bp-0x33,v15为bp-0x9,因此字符串长度为0x33 - 0x9 = 42

这时我们在v4上y一下,然后在弹出来的窗口上填入我们推断出来的数据类型char v4[42],再点OK,这时IDA就能正确的识别出来v4的数据类型了,如下图:

payload1:

通过两次rop,实现get shell

payload2:

利用ROPgadget找到了一个pop4ret的gadget,利用pop4ret平衡堆栈,用一个ropchain实现getshell

需要注意,对于execve的第一个参数”/bin/sh”,我们可以用先写入一个固定地址(如bss段)的方式迂回传参。

0x04 toooomuch

放松心情的题,用IDA很容易可以得到通过验证的passcode,然后用 二分/XJB猜 猜对数字后,就可以拿到flag

0x05 toooomuch-2

题目已经提示利用缓冲区溢出通过shellcode来get shell,IDA查看程序流程,在gets函数中存在溢出

这样只要把通过ROP把shellcode写到bss段,再返回bss段执行shellcode即可

payload:

0x06 echo

看到echo,第一反应就是格式化字符串的题,分析文件后果然发现了很明显的格式化字符串漏洞

并且可以通过while循环多次利用,很经典的利用方式,先泄露出system_got中的system函数真实地址,再覆写printf_got为system_addr,之后通过fgets读入”/bin/sh"时,printf("/bin/sh”)已经相当于system("/bin/sh”),即可get shell

payload:

需要注意如果leak的payload是p32(system_got) + “%7$s”的形式,那么泄露出的system_addr是从第4位到第8位(p32(system_got))占据了前4位,另外如果p32(system_got)中有\x00等bad char截断printf的话,可以调整payload形式为%8$s + p32(system_got)

0x07 echo2

很明显这一题也是格式化字符串的漏洞,与上一题的不同在于:

  1. 64位,这意味着fmtstr_payload函数极有可能因为\x00不能用
  2. 开启了PIE,也就是说got表等地址都是随机的,但因为elf中各个部分的偏移是固定的,因此可以通过泄露elf基址的方法来确定其他部分的真实地址

通过调试看出可以利用%41$p和%43$p泄露main地址与__libc_start_main的地址(0x23 + 6, 0x25 + 6):

泄露elf_base与libc_base的函数如下:

1
2
3
4
5
6
7
8
def getAddr():
    io.sendline("%41$p..%43$p..")
    elf_base = int(io.recvuntil("..", drop = True), 16) - 74 - 0x9b9#nm ./echo2
    libc_base = int(io.recvuntil("..", drop = True), 16) - 240 - libc_offset # this is for remote
    #  libc_base = int(io.recvuntil("..", drop = True), 16) - 241 - libc_offset #this is for local
    log.info("elf_base -> 0x%x" % elf_base)
    log.info("libc_base -> 0x%x" % libc_base)
    return elf_base + exit_got, libc_base + one_gadget

获得elf_base与libc_base基址后,按照正常的格式化字符串思路来就行,比如可以用one_gadget地址覆写exit函数的got表地址.

0x08 echo3

漏洞和逻辑都很简单, 非栈区的格式化字符串利用, 利用方式可以参考 hitcon-training 的 lab9, 那道题里我把调试的过程也放到代码里了, 比较容易看清楚逻辑 同时这道题有一个随机数来打乱栈的布局, 导致我们没办法确定每次格式化字符串的 offset, 但仔细观察后发现随机数只有几个, 可以通过爆破的方法来确定

 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
inndy_echo3 [master] cat rand.py 
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'

f = lambda x: 16 * (((x & 0x3039) + 30) / 0x10)

allrand = []
for i in xrange(0xffff):
    allrand.append(f(i))

for i in set(allrand):
    print "{:#x}\t{}\t{}".format(i, allrand.count(i), float(allrand.count(i)) / sum(set(allrand)))
inndy_echo3 [master] python rand.py 
0x20	4096	0.0330749354005
0x1010	2048	0.0165374677003
0x1050	2048	0.0165374677003
0x1030	4096	0.0330749354005
0x2010	2048	0.0165374677003
0x30	4096	0.0330749354005
0x50	2048	0.0165374677003
0x40	4096	0.0330749354005
0x3050	2047	0.0165293927649
0x3010	2048	0.0165374677003
0x2020	4096	0.0330749354005
0x1040	4096	0.0330749354005
0x1020	4096	0.0330749354005
0x3020	4096	0.0330749354005
0x10	2048	0.0165374677003
0x2040	4096	0.0330749354005
0x3030	4096	0.0330749354005
0x3040	4096	0.0330749354005
0x2050	2048	0.0165374677003
0x2030	4096	0.0330749354005

同时本地可以使用 gdb.debug() 来指定随机数方便本地调试, 具体的实现可以参考我的 exp 拿到 flag 后与站主交流, 发现我用了非预期解, 也贴一下站主的 exp

0x09 smash-the-stack

典型的canary leak,覆盖__libc_argv[0]为flag在内存中地址,触发__stack_chk_fail函数即可泄露flag

放一篇学习链接:http://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/

payload:

或者可以用更暴力的方法,不计算argv[0]到缓冲区的距离,用一个较大的值直接覆盖过去即可:

payload:

需要注意的是write函数的长度是由用户输入决定的,给buf一个较小的值即可

0x0A onepunch

本来以为onepunch的意思是构造好payload一发get shell, 后来才发现是一个字节一个字节的打

题中有一个任意地址写一个字节的漏洞,刚开始觉得无从下手,后来调试的时候发现代码段为rwxp权限,才觉得柳暗花明。

既然可以在代码段任意地址写,就意味着我们可以为所欲为的修改代码流程,因此就可以将

.text:0000000000400767 jnz short loc_400773

修改为

.text:0000000000400767 jnz 0x40071D

这样就构成了一个循环,接下来在合适的位置写shellcode,然后跳转到shellcode即可

payload:

至于怎么确定要patch的地址和值,我推荐keypatch,用keypatch修改完伪代码后,通过将  Options -> General ->Number of Opcode byte修改为非0值对比修改前后的字节码即可

0x0B rsbo

这道题的利用方法倒是第一次见到,对于read和write的第一个参数fd(文件描述符),fd = 0时代表标准输入stdin,1时代表标准输出stdout,2时代表标准错误stderr,3~9则代表打开的文件。这一题的利用方式利用方式就是利用rop先用open函数打开位于/home/rsbo/的flag,然后再用read(3, )把flag写到一个固定地址上,最后用write输出

payload:

0x0C rsbo-2

和 rsbo-1 完全一样, 这次要 get shell 了, 也很简单, binary 中有 write@plt, 因此可以通过 rop 来 leak write@got 等函数, 进而确定 libc.address, 然后再来一次 rop 执行 system("/bin/sh”) 就行了 因为远程端口也是一样的, 因此直接做 rsbo-2 即可

这道题是回头又看的, 以前居然因为这种难度的题卡了那么久= =

0x0D leave_msg

经过分析代码的逻辑,绕过下边的限制,就可以覆写got表:

1
2
if ( v4 <= 64 && nptr != 45 )
  dword_804A060[v4] = (int)strdup(&buf);

而v4=atoi(&ntpr),经过搜索atoi函数会跳过字符串开头的空白字符,因此只要构造**(空格)-16**就可以同时绕过上边的两个限制来覆写puts的got表,然后再用\x00绕过strlen的限制就可以执行shellcode,payload如下:

此时的程序流程如下:

至于0x36是怎么来的,我是调试看出来的,如果哪位表哥有静态计算的方法,还请不吝赐教!

0x0E stack

这个题开了全保护,第一眼看上去挺吓人,但其实漏洞很容易发现,pop时并没有对下标作出检查,这就意味着我们可以通过一直pop利用数组越界从栈上leak,先通过调试看栈结构

可以看出,通过一直pop可以泄露**_IO_2_1_stdout_**的地址,进而确定libc的装载基址和one_gadget地址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def getBase():
    #  debug()
    for i in xrange(15):
        io.sendlineafter("Cmd >>", "p")
        io.recvuntil("-> ")

    libc_base = (int(io.recvuntil("\n", drop = True)) & 0xffffffff)- libc.symbols["_IO_2_1_stdout_"]
    info("libc_base -> 0x%x" % libc_base)
    one_gadget = libc_base + one_gadget_offset

    return one_gadget
    #  return libc_base

通过main+27, main+427等地址可以泄露elf的装载基址(当然并没有用到),并且经过观察,main+27, main+427分别是__x86_get_pc_thunk_dx和stack_push的返回地址,这样我们只需要把main+427覆盖为one_gadget的地址,再次调用stack_push时,就可以返回到one_gadget,进而get shell,有一个坑点是0xf段的地址会发生数据溢出,需要我们转成int32的类型。

虽然听起来很麻烦但只要耐心调试并不难,这个题目的flag也说明了本题的重点就是leak from stack。

关于这个题有两点值得一提:

  1. 刚打开文件使用F5时报错

    很容易搜索到这是因为函数的参数个数不匹配,我们定位到0x78D,发现540这个函数有一个参数,但IDA并没有正确识别

    手动指定一个参数后(快捷键y指定类型),这个函数的报错就解决了

    同样的方法修复之后的报错,就可以F5查看伪代码了

    实现查看伪代码后,可以通过查看函数的具体实现进一步识别函数和修正函数参数

    如上图的578函数,可以看出实际调用了hex(8128 + 56)这个地址,而这个地址在IDA中可以看出是scanf的地址,这样我们就可以通过scanf的参数列表进一步修正参数了,附一张我修复之后的图

    这样在通过对比题目给出的源码,就很容易分析程序的功能了

    1. 这个题目开启了PIE保护,利用pwn.gdb.attach调试的时候和没有开启PIE保护的有些不同。

      以前我调试开启了PIE保护elf的方式是Uriel师傅教我的先找elf装载基址

 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
	from pwn import *
	import sys, os
	import re

	wordSz = 4
	hwordSz = 2
	bits = 32
	PIE = 0
	mypid=0
	context(arch='amd64', os='linux', log_level='debug')
      def leak(address, size):
         with open('/proc/%s/mem' % mypid) as mem:
            mem.seek(address)
            return mem.read(size)
    
      def findModuleBase(pid, mem):
         name = os.readlink('/proc/%s/exe' % pid)
         with open('/proc/%s/maps' % pid) as maps:
            for line in maps:
               if name in line:
                  addr = int(line.split('-')[0], 16)
                  mem.seek(addr)
                  if mem.read(4) == "\x7fELF":
                     bitFormat = u8(leak(addr + 4, 1))
                     if bitFormat == 2:
                        global wordSz
                        global hwordSz
                        global bits
                        wordSz = 8
                        hwordSz = 4
                        bits = 64
                     return addr
         log.failure("Module's base address not found.")
         sys.exit(1)
    
      def debug(addr = 0):
          global mypid
          mypid = proc.pidof(r)[0]
          raw_input('debug:')
          with open('/proc/%s/mem' % mypid) as mem:
              moduleBase = findModuleBase(mypid, mem)
              gdb.attach(r, "set follow-fork-mode parent\nb *" + hex(moduleBase+addr))    

但做这道题题时发现pwn.gdb.attach只有第一个参数是必须的

这样就可以先进入gdb,通过vmmap找到elf基址后在下断点进行调试了

0x0F very_overflow

这个题目给了源码,分析起来方便了不少。这个题的漏洞也很容易发现,虽然申请了长度为 128 * (sizeof(buffer))的缓冲区,但可以无限的add_note,这就意味着我们可以先重复add_note耗尽缓冲区,然后继续add_note和show_note时,就可以leak类似__libc_start_main这些信息来确定libc装载基址了,有了libc装载基址后,通过rop构造system("/bin/sh”)或者one_gadget都可以求解

至于add_note多少次,通过调试可以很清楚的算出来

另外就是刚开始本地可以get shell,但远程连接很容易超时,后来把context.log_level换成了info,减少了打印花费的时间,又把io.sendlineafter换成了直接io.sendline,就不容易超时了

0x10 notepad

这道题目的菜单输入那里乍看起来很奇怪,因为执行哪个分支是根据ord(input) - ord(‘a’) 来决定的,当输入小于a时又没有做容错处理(似乎本题也只有这一个漏洞). 仔细分析,在notepad_open函数里,有一个根据输入执行特定函数的机会,这样只要我们精心构造chunk布局和输入,让当前notepad_open函数根据我们的输入执行特定函数时,执行上一个chunk中我们填好的函数,这样我们就有了执行任意函数的能力,一图胜千言,用下图来表示此过程:

 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
 +-------+-------+
 |prev0  |size0  |
 +-------+-------+
 |show_func_ptr  | <- chunk0
 +---------------+
 |destory_fun_ptr|
 +---------------+
 |rdonly         |
 +---------------+      
 |size           |
 +---------------+
 |....           |
 |data           |
 |evil_func      |
 |....           |
 +-------+-------+
 |prev1  |size1  |
 +-------+-------+
 |show_func_ptr  | <- chunk1
 +---------------+
 |destory_fun_ptr|
 +---------------+
 |rdonly         |
 +---------------+
 |size           |
 +---------------+
 |....           |
 |data           |
 ++--------------+
 如上图,题目希望我们在notepad_open(chunk1)时,退出时使用chunk1的show_func_ptr或者destory_fun_ptr函数指针,但因为没有检测输入为负,我们可以在前一个chunk(chunk0)中的data中写好evil_func的地址,控制输入,输入合理的负数执行该函数,这样只要合理的控制,我们就有了执行任意函数的能力

我的做法分为以下几步:

  1. 先通过该漏洞 free 一个 chunk,造成 uaf

  2. 再利用该漏洞 puts freed_chunk 来 leak libc

  3. gets读入 $0\0/sh\0

  4. 执行 system(“$0”)

0x11 mailer

很明显,gets那里能够arbitrary overflow, 又没开NX保护, 直接利用arbitrary overflow修改top_chunk,使用house of force修改got到shellcode的地址即可, 没什么好说的

0x12 tictactoe-1

给的elf文件逆起来比较繁琐,通过反编译可以找到棋的源码,了解了程序的大体流程后,可以发现在落子时可以通过数组越界覆写GOT表

但因为程序有一个判负退出的功能,因此经过实验最多只能写三个字节,但对第一题而言,三个字节已经足够overwrite memset@got为打印flag的地址

第一题很快就解决了,至于tictatoe-2,虽然get shell拿到了flag,但根据flag形式需要用ret2dl_solve,等我用ret2dl_solve解决时再来补wp

reverse

0x00 helloworld:

0x01 simple:

都是水题,不再细说

0x03 pyyy

这一题用uncomplye6或者在线网站反编译后,得到的python代码都有大量的lambda操作,读起来十分费力,并且代码不能直接运行,仔细观察,代码中有一行

​ this_is = ‘Y-Combinator’

似乎是在暗示什么,google了一发,在 https://rosettacode.org/wiki/Y_combinator#Python 找到了Y-Combinator的介绍,即利用lambda等操作实现递归,于是按照网站上给出的python实例修改了代码,使其能够正确执行。再仔细观察,发现只要我们每轮的输入都和程序每轮计算的值相等即可得到flag,因此直接修改python代码,让输入等于程序计算好的值,修改好的代码如下:

 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
# uncompyle6 version 2.12.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.13 (default, Jan 19 2017, 14:48:08) 
# [GCC 6.3.0 20170118]
# Embedded file name: pyyy.py
# Compiled at: 2016-06-12 01:14:31
from fractions import gcd
__import__('sys').setrecursionlimit(1048576)
data = 'Tt1PJbKTTP+nCqHvVwojv9K8AmPWx1q1UCC7yAxMRIpddAlH+oIHgTET7KHS1SIZshfo2DOu8dUt6wORBvNVBpUSsuHa0S78KG+SCQtB2lr4c1RPbMf0nR9SeSm1ptEY37y310SJMY28u6m4Y44qniGTi39ToHRTyxwsbHVuEjf480eeYAfSVvpWvS8Oy2bjvy0QMVEMSkyJ9p1QlGgyg3mUnNCpSb96VgCaUe4aFu4YbOnOV3HUgYcgXs7IcCELyUeUci7mN8HSvNc93sST6mKl5SDryngxuURkmqLB3azioL6MLWZTg69j6dflQIhr8RvOLNwRURYRKa1g7CKkmhN4RytXn4nyK2UM/SoR+ntja1scBJTUo0I31x1wBJpT4HjDN47FLQWIkRW+2wnB3eEwO5+uSiQpzA8VaH7VGRrlU/BFW4GqbaepzKPLdXQFBkNyBKzqzR/zA2GIrYbLIVScWJ19DqJCOyVLGeVIVXyzN1y327orYL2Ee3lRITnE3FouicRStaznIcw8xmxvukwVMRZIJ/vTu8Zc1WQIYEIFXMHozGuvzZgROZTyFihWNRCBBtoP9DJJALJb0pA1IKIb2zLh+pwGF40Y6y93D6weKejGPO+A0DBXH9vuLcCcCIvr/XPQhO3jLKCBN+h9unuJKW3dyWxyaVPdR2V+BTw10VXolo7yaTH1GbR4TiVSB308mBOMwfchwihEe7RdMXvmXgaGarKkJe0NLUCd8jwhYII+WymjxO/xOz/ppOvNfAyIQksW0sggRPQTlgXSZ7MIVA1h66sGNljJ833MoFzWof3azLabaz1OrAJFqYXBg/myDsy1tV6rULSQ82hVR/TNnSmBGvyEDJTrLSwHyj78NOrW4mUnlLGBnAgWfw6pW2lRK2jkNX9NM6DfLsRK8lwl85UP8CZSuNdcLmLwHTVMZGm/cNkZCtWRBlZqEggxGdIO44D+f4y6ysnAk5/QzEwjIuecxEOb0jyV6dFui8g0c3Oxlhzcli0X8ToJFyeQRv1N9nokYZ07tFlG6m18kCToKz1qiH1U7kljXa6SvdORur5dWYLQ//gwhwppe7JlNda/cEoh92h96wRZDv1dSK/f1vz+mUeUyUlFY0iMjfw5eBXWZppNZi3ZtJcq5kllM2ACVFcxQWI3azM3ArOcqjosoiPjNoDYgKh7w4k2Cd0kLYEHscz/njtJ1KEcwLtqs4nJ+gB2r4V9g03YgvY5E8JJtfJMKdaTedjtvEuif8FNlCK9DMnL1iLpWptJbdfO83Y7Y46XCqjZFBI5o9Qtb78nLhMEM5/YTaNOM/wE/oJl5HI/i1X6kW3PKCsVubRkOkc2xawl6NYdLETjLvmrGhhI'
a = 138429774382724799266162638867586769792748493609302140496533867008095173455879947894779596310639574974753192434052788523153034589364467968354251594963074151184337695885797721664543377136576728391441971163150867881230659356864392306243566560400813331657921013491282868612767612765572674016169587707802180184907L
b = 166973306488837616386657525560867472072892600582336170876582087259745204609621953127155704341986656998388476384268944991674622137321564169015892277394676111821625785660520124854949115848029992901570017003426516060587542151508457828993393269285811192061921777841414081024007246548176106270807755753959299347499L
c = 139406975904616010993781070968929386959137770161716276206009304788138064464003872600873092175794194742278065731836036319691820923110824297438873852431436552084682500678960815829913952504299121961851611486307770895268480972697776808108762998982519628673363727353417882436601914441385329576073198101416778820619L
d = 120247815040203971878156401336064195859617475109255488973983177090503841094270099798091750950310387020985631462241773194856928204176366565203099326711551950860726971729471331094591029476222036323301387584932169743858328653144427714133805588252752063520123349229781762269259290641902996030408389845608487018053L
e = 104267926052681232399022097693567945566792104266393042997592419084595590842792587289837162127972340402399483206179123720857893336658554734721858861632513815134558092263747423069663471743032485002524258053046479965386191422139115548526476836214275044776929064607168983831792995196973781849976905066967868513707L
F = (a, b, c, d, e)
m = 8804961678093749244362737710317041066205860704668932527558424153061050650933657852195829452594083176433024286784373401822915616916582813941258471733233011L
g = 67051725181167609293818569777421162357707866659797065037224862389521658445401L
z = []
for i, f in enumerate(F):
    n = pow(f, m, g)
    #https://rosettacode.org/wiki/Y_combinator#Python
    this_is = 'Y-Combinator'
    #  l = (lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))
    Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
    fun = lambda f: lambda x: (1 if x < 2 else f(x - 1) * x % n)
    l = Y(fun)(g % 27777)
#   Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
#   fac = lambda f: lambda n: (1 if n<2 else n*f(n-1))
#  >>> [ Y(fac)(i) for i in range(10) ]
#  [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
#  >>> fib = lambda f: lambda n: 0 if n == 0 else (1 if n == 1 else f(n-1) + f(n-2))
#  >>> [ Y(fib)(i) for i in range(10) ]
#  [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
    #  print l
    #  c = raw_input('Channenge #%d:' % i)
    c = l
    if int(c) != l:
        print 'Wrong~'
        exit()
    z.append(l)

z.sort()
gg = '(flaSg\'7 \\h#GiQwt~66\x0csxCN]4sT{? Zx YCf6S>|~`\x0c$/}\'\r:4DjJFvm]([sP%FMY"@=YS;CQ7T#zx42#$S_j0\\Lu^N31=r\x0b\t\tjVhhb_KM$|6]\nl!:V\rx8P[0m ;ho_\rR(0/~9HgE8!ec*AsGd[e|2&h!}GLGt\'=$\x0cbKFMnbez-q\\`I~];@$y#bj9K0xmI2#8 sl^gBNL@fUL\x0b\\9Ohf]c>Vj/>rnWXgLP#<+4$BG@,\'n a_7C:-}f(WO8Y\x0c2|(nTP!\'\\>^\'}-7+AwBV!w7KUq4Qpg\tf.}Z7_!m+ypy=`3#\\=?9B4=?^}&\'~ Z@OH8\n0=6\x0b\tv\nl!G\'y4dQW5!~g~I*f"rz1{qQH{G9\x0c\'b\x0cp\x0bdu!2/\\@i4eG"If0A{-)N=6GMC<U5/ds\rG&z>P1\nsq=5>dFZUWtjv\tX~^?9?Irwx\\5A!32N\x0bcVkx!f)sVY Men\x0c\'ujN<"LJ\x0c5R4"\\\\XPVA\'m$~tj)Br}C}&kX2<|\np3XtaHB.P\'(E 4$dm!uDyC%u ["x[VYw=1aDJ (8V/a!J?`_r:n7J88!a25AZ]#,ab?{%e\x0b]wN_}*Q:mh>@]u\t&6:Z*Fmr?U`cOHbAf7s@&5~L ,\tQ18 -Hg q2nz%\x0ccUm=dz&h1(ozoZ)mrA=`HKo\n\'rXm}Z-l3]WgN\\NW<{o=)[V({7<N1.-A8S"=;3sderb\tOZ$K\r0o/5\x0bMc76EGCWJ3IQpr7!QhbgzX8uGe3<w-g\'/j\'\tM4|9l?i&tm_\n57X0B2rOpuB@H@%L_\r)&/q=LZa(%}""#if#Kq74xK?`jGFOn"8&^3Q-\r#]E$=!b^In0:$4VKPXP0UK=IK)Y\rstOT40=?DyHor8j7O\\r/~ncJ5];cCT)c?OS0EM5m#V(-%"Tu:!UsE],0Dp  s@HErS]J{%oH54B&(zE.(@5#2k\tJnNlnUEij\\.q/3HBpJNk*X(k5;DlqK\'\'fX\r}EBk_7\x0b:>8~\t+M@WJx.PO({/U}1}#TqjreG\nN{\rX>4EsJr0Pn\\Z\\aL/-U<<{,Q;j\tF=7f\')+wH:p{G=_.s\\t-\x0bI\x0c*y\t1P:Y|/2xE<uo]~$>5k]FW+>fR<QA"(Fj[LL(hzfQo#PJ;:*0kB~3]9uL[o.xue:VQ\t;9-Tu\tq|mzzhV_okP\t,d\rQ`]5Gf\x0c#gXB\x0cAH|)NI|K=KW-&p-<b"3e.rO\x0cuK=\x0c^\r+MuLxCJ`UKaD\x0bBH&n+YVajZ(U7pwWtto3T10VLHwSJ\rK\t}\'F$l1:b2Bd\na=#t0iq}#!{1_)w$}<Dp(borC\'\t?r6;,+k;a(Q3@B?RCWYEDrjZe![x=n_%S]rl{&fLr*mgCD;92/nNsaxKy/;\nr]sPK=`+YP>MmfB\n8O4/"}nE7r*=41f2\t37>K\'s$wpl;qS[`qzu\x0b\t\nuaU|b,C`4& dRN~]7DnuTb2FhNHV!#Z2Hho\x0b[%.{O\t$q0\x0ch_@?w@b8[I^{JL|O8]i8{p)A.w)14qK3JoyF%licZ~ga\rW[L:W\rtIvfWJjZUOvB\rS.Beav3!-@bw|PexJ Pcw1\ry6!63B}]J])6fak/3r]W\tMeXt[uc(1_U lys{a1X\r%)[wwP3rhgNW{*d~_E%Q2htCt5ha@l0^0=\x0bwT\ni4/V;_\nM1rb?w~Q)Dli4u\n`}1+D8"\t`@V~$9l$Uy**VnI (@Ga0<RxfmoNgJTtE-aLH\rE5fMy7rk$)V\rL2Fv/AivOa"\nuX|70Xrw^D]%i%JyT\x0cc%cwZ/Wbp=IiY;/@nFEe>3=tM;K*`fReGoc5V/Ri?nXZ-RW)\'\t<\x0cV>@X@-Ei4%sO%},B_pjc`s"@oKCmdgDhjUZT@?mb\'?Q:F\x0bLJkPgjaFAc=rbrjAz$Zz\x0cq0GU!")xFOEF(x!3M\t:l83|}}HgGJJ#eT/I\x0b[|lK_n+;Wi/N^B4LzL.a(gVWq,zO6\'S|tb>RX` ca*CO<w\x0ci =wc1,M~\x0bc`FYEs\r){+Ll8[I9-88m\t\\iK/\\hno-C[vX*3Hx:%:K\rt\x0cW!tj\'SOhqxP|k7cw Hm?I@?P\'HmapG7$0#T(Auz]sjmd#\rFP/}53@-Kvmi(d%dZKLZ2LK\'e_E\x0bQmR 5/(irq4-EUyp<hB?[\tnU:p*xuzASM'

Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
fun = lambda f: lambda n: (1 if n < 3 else f(n - 1) + f(n - 2))

print ''.join((gg[Y(fun)(i + 2)] for i in range(16))) % ''.join((data[pow((gcd(z[i % 5], z[(i + 1) % 5]) * 2 + 1) * g, F[i % 5] * (i * 2 + 1), len(data))] for i in range(32)))
#   Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
#   fac = lambda f: lambda n: (1 if n<2 else n*f(n-1))
#  >>> [ Y(fac)(i) for i in range(10) ]
#  [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
#  >>> fib = lambda f: lambda n: 0 if n == 0 else (1 if n == 1 else f(n-1) + f(n-2))
#  >>> [ Y(fib)(i) for i in range(10) ]
#  [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
#  okay decompiling pyyy.pyc

运行即可

0x04 accumulator

这个题目中check函数的F5代码有点混乱,单看伪代码很难分析,但通过调试可以快速确定check函数的整体流程是对输入的每一位求和,然后与0x601080这个数组进行比较,在这个过程中更新0x6013C0和0x6013B0两个全局变量用于寻址,程序的整体流程是先check(sha512(input)),然后check(input),因为sha512是哈希函数,因此放弃了逆向求解。仔细分析,在check(input)时,使用的也是对input每一位求和,然后与数组比较的方法,而与第一次check(sha512(input))相比仅仅是两个用于寻址的全局变量不同,这样我们只要对数组逐位做差即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'

data = [195, 255, 493, 584, 799, 929, 946, 1086, 1180, 1184, 1421, 1595, 1805, 1846, 2081, 2320, 2430, 2605, 2727, 2972, 3213, 3403, 3418, 3649, 3712, 3950, 3989, 4193, 4228, 4394, 4523, 4624, 4706, 4935, 4999, 5072, 5106, 5291, 5510, 5536, 5644, 5751, 5993, 6118, 6126, 6198, 6211, 6410, 6469, 6609, 6647, 6752, 6978, 7010, 7053, 7106, 7274, 7468, 7563, 7673, 7706, 7956, 8146, 8187, 8257, 8333, 8398, 8469, 8592, 8640, 8693, 8742, 8793, 8844, 8901, 8953, 9007, 9062, 9113, 9161, 9215, 9317, 9374, 9429, 9483, 9540, 9591, 9644, 9692, 9741, 9792, 9846, 9944, 9996, 10045, 10144, 10195, 10246, 10294, 10350, 10402, 10450, 10551, 10652, 10750, 10849, 10946, 11045, 11096, 11147, 11202, 11304, 11353, 11451, 11507, 11605, 11653, 11753, 11852, 11900, 11951, 12052, 12105, 12161, 12259, 12360, 12409, 12461, 12563, 12664, 12718, 12775, 12823, 12921, 12970, 13020, 13071, 13173, 13227, 13276, 13374, 13422, 13521, 13569, 13667, 13718, 13771, 13873, 13972, 14029, 14080, 14179, 14278, 14377, 14432, 14482, 14531, 14579, 14627, 14679, 14732, 14789, 14840, 14894, 14951, 15052, 15154, 15210, 15263, 15314, 15363, 15460, 15509, 15610, 15666, 15763, 15818, 15916, 15968, 16018, 16075, 16132, 16233, 16288, 16386, 16443, 16543, 16600, 16655, 16703, 16801, 16858, 16955, 17005, 17056, 17153, 17250, 17375]

flag = ""
for i in xrange(1, len(data)):
    flag += chr(data[i] - data[i - 1])

print flag

再介绍一个IDA使用的trick,在提取数组时,先在数组的第一位上点d,使数组的类型转换成dd,然后再点a,使IDA把这一段连续的数据转成数组,然后可以使用alt+L快速选中数组范围(alt+L,鼠标滚轮,afl+L),然后通过shift+E就可以快速导出我们想要选择的数据了

0x05 gccc

该题的逻辑很简单,只有一个输入,下载文件后发现gccc.exe为.Net(C#)架构,因此用C#的反编译工具(这里用了)反编译得到题目代码如下:

 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
// GrayCCC
public static void Main()
{
	Console.Write("Input the key: ");
	uint num;
	if (!uint.TryParse(Console.ReadLine().Trim(), out num))
	{
		Console.WriteLine("Invalid key");
		return;
	}
	string text = "";
	string text2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{} ";
	int num2 = 0;
	byte[] array = new byte[]
	{
		164, 25, 4, 130, 126, 158, 91, 199, 173, 252, 239, 143, 150, 251, 126, 
		39, 104, 104, 146, 208, 249, 9, 219, 208, 101, 182, 62, 92, 6, 27, 5, 46
	};
	byte b = 0;
	while (num != 0u)
	{
		char c = (char)(array[num2] ^ (byte)num ^ b);
		if (!text2.Contains(new string(c, 1)))
		{
			Console.WriteLine("Invalid key");
			return;
		}
		text += c;
		b ^= array[num2++];
		num >>= 1;
	}
	if (text.Substring(0, 5) != "FLAG{" || text.Substring(31, 1) != "}")
	{
		Console.WriteLine("Invalid key");
		return;
	}
	Console.WriteLine("Your flag is: " + text);
}

可以看到,逻辑很简单,对输入进行了32轮验证,通过每一层验证即可得到flag.

但逻辑简单并不意味着容易解决,仔细分析代码可以看出解题的关键在求出num的值,num经过32次右移一位后等于0,因此取值范围为**[2 ^ 31, 2 ^ 32)**, 在该范围内找到满足32轮验证的值即可,但在找到该值时遇到了问题,如果单纯爆破的话,2 ^ 32 - 2 ^ 31次爆破远远超出了能接受的范围,因此考虑用z3来解决这个多约束问题.

该题的约束条件有如下几个:

  • 0~5位为"FLAG{”
  • 最后一位(31)为”}”
  • 6~30位需在"ABCDEFGHIJKLMNOPQRSTUVWXYZ{} “内

因此可以写出z3的代码如下:

 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
def getNum():
	b = 0
	num2 = 0
	# 2 ** 30 <= num < 2 ** 31
	s = Solver()
	num = BitVec('num', 64)
	s.add(num >= 2 ** 31)
	s.add(num < 2 ** 32)
	# s.add(num > 1510650850)

	for i in xrange(32):
		if i < 5:
			s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('FLAG{'[i]))
		elif 5 <= i < 31:
			s.add(
					Or(	
						And(
							((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) >= 65, 
							((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) <= 90, 
						),
						
						# ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('{'),
						# ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'),
						((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord(' ')

					)
				)
                elif i == 31:
                        s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'))
		b ^= array[num2]
		b &= 0x7f
		num2 += 1
		num >>= 1

	if s.check() == sat:
		print s.model()
                #bug
		#  print s.model()[num].as_long()
	# while s.check() == sat:
		# print s.model()[num]
		# s.add(Or(num != s.model()[num].as_long()))

运行,num的值就秒出了

注释掉的部分是一个还没解决的bug,本来是想通过添加约束跑出所有的可行解,但可能是因为用了BitVec导致在数据类型的转换上有些问题,现在只跑出了一组解.等到考完试再来修这个bug

有了num的值,接下类有很简单了,可用python求解

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = "M4x"

import pdb
from z3 import *
array = [164,25, 4, 130, 126, 158, 91, 199, 173, 252, 239, 143, 150, 
251, 126, 39, 104, 104, 146, 208, 249, 9, 219, 208, 101, 
182, 62, 92, 6, 27, 5, 46]
# print len(array)

def getNum():
	b = 0
	num2 = 0
	# 2 ** 30 <= num < 2 ** 31
	s = Solver()
	num = BitVec('num', 64)
	s.add(num >= 2 ** 31)
	s.add(num < 2 ** 32)
	# s.add(num > 1510650850)

	for i in xrange(32):
		if i < 5:
			s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('FLAG{'[i]))
		elif 5 <= i < 31:
			s.add(
					Or(	
						And(
							((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) >= 65, 
							((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) <= 90, 
						),
						
						# ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('{'),
						# ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'),
						((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord(' ')

					)
				)
                elif i == 31:
                        s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'))
		b ^= array[num2]
		b &= 0x7f
		num2 += 1
		num >>= 1

	if s.check() == sat:
		print s.model()
                #bug
		#  print s.model()[num].as_long()
	# while s.check() == sat:
		# print s.model()[num]
		# s.add(Or(num != s.model()[num].as_long()))

def getFlag():
	text2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{} "
	num = 3658134498
	num2 = 0
	b = 0 
	flag = ""

	while num:
		c = chr((array[num2] ^ (num & 0x7f) ^ b) & 0x7f)
		if c not in text2:
			print ord(c)
		flag += c
		b ^= array[num2]
		num2 += 1
		num >>= 1
		print flag

#  getNum()
getFlag()

也可简单的修改反编译得到的c#代码,运行C#得到flag(推荐该方法)

 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
// GrayCCC
using System;
namespace GrayCCC
{
	class GCCC
	{
		public static void Main()
		{
			// Console.Write("Input the key: ");
			// uint num;
			// if (!uint.TryParse(Console.ReadLine().Trim(), out num))
			// {
			// 	Console.WriteLine("Invalid key");
			// 	return;
			// }
			string text = "";
			string text2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{} ";
			int num2 = 0;
			uint num = 3658134498;
			byte[] array = new byte[]
			{
				164,25, 4, 130, 126, 158, 91, 199, 173, 252, 239, 143, 150, 
				251, 126, 39, 104, 104, 146, 208, 249, 9, 219, 208, 101, 
				182, 62, 92, 6, 27, 5, 46
			};
			
			byte b = 0;
			while (num != 0u)
			{
				char c = (char)(array[num2] ^ (byte)num ^ b);
				if (!text2.Contains(new string(c, 1)))
				{
					Console.WriteLine("Invalid key");
					return;
				}
				text += c;
				b ^= array[num2++];
				num >>= 1;
				Console.WriteLine(text);
			}
			if (text.Substring(0, 5) != "FLAG{" || text.Substring(31, 1) != "}")
			{
				Console.WriteLine("Invalid key");
				return;
			}
			Console.WriteLine("Your flag is: " + text);
		}
	}
}

可以看出,通过z3求解此类多约束问题能极大地提高效率.

另外听Kira师傅说这道题也可以逐位爆破,也等到考完试再来填坑吧.

0x06 ccc

IDA F5后程序的流程很清楚,唯一一处有困惑的是不知道下图中的crc32函数是按照什么规则加密的并且返回值是什么

根据for循环中的i = 3, i += 3等信息可以推测v3是每3位计算一次,但究竟是a2[0: 3], a2[3: 6]这种方式还是a2[0: 3], a2[0: 6]这种方式还不得而知,静态分析crc32函数有很麻烦。这时,就可以用gdb动态调试来验证加密的方式。

首先在伪代码页右键,Copy to Assembly,方便我们在汇编页快速定位,如下定位到关键地址0x8048558

直接在gdb中下断点,r运行到断点,输入42位字符,此时程序运行到了断点处

n,执行下一步,此时crc32的返回值即保存在了eax寄存器中(函数调用约定)

可以发现,第一次调用crc32函数时,函数对输入的前3位做了计算,接下来调用的计算方式可以类比动态调试得到,最终发现crc32是按照a[0: 3], a[0: 6], a[0: 9]这样的方式进行计算的,每次循环计算的结果再与hashes中的只比较即可

再补充一个trick,这个操作是跟w1tcher表哥学到的

我们知道crc32计算的结果是8位16进制数,而IDA默认以两位16进制数显示数据,这是我们可以在第一个数据上右键,data,使前四个转成8位16进制数形式(或者快捷键D),然后再右键,array,点击OK,即可默认把下边的数据按第一组的形式展现出来

每次爆破3位即可,脚本如下:

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'
import binascii
import string
import pdb

hashes = [0x0D641596F, 0x80A3E990, 0x0C98D5C9B, 0x0D05AFAF, 0x1372A12D, 0x5D5F117B, 0x4001FBFD, 0x0A7D2D56B, 0x7D04FB7E, 0x2E42895E, 0x61C97EB3, 0x84AB43C3, 0x9FC129DD, 0x0F4592F4D]

def crc(s, cnt):
    for a in dic:
        for b in dic:
            for c in dic:
                ans = s + a + b + c
                #  print tmp
                if (binascii.crc32(ans) & 0xffffffff) == hashes[cnt]:
                    #  pdb.set_trace()
                    return ans

#  dic = string.ascii_letters + string.digits + " _-{},"
dic = string.printable
#  ans = 'FLAG{'
ans = ''
cnt = 0
while True:
    try:
        ans = crc(ans, cnt)
        cnt += 1
        print ans
    except:
        break

0x07 bitx

IDA中可以看到,输入是与0x804A040地址上的一组数据进行比较的(IDA伪代码页,右键,Hide casts可以隐藏数据类型,看起来美观不少),程序逻辑很简单,直接放脚本

提取数据可以直接粘贴复制,也可以直接用idc或者ida python等脚本,如下的idc脚本即可dump出内存中的数据

解题脚本如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'

data = [0x8f, 0xaa, 0x85, 0xa0, 0x48, 0xac, 0x40, 0x95, 0xb6, 0x16, 0xbe, 0x40, 0xb4, 0x16, 0x97, 0xb1, 0xbe, 0xbc, 0x16, 0xb1, 0xbc, 0x16, 0x9d, 0x95, 0xbc, 0x41, 0x16, 0x36, 0x42, 0x95, 0x95, 0x16, 0x40, 0xb1, 0xbe, 0xb2, 0x16, 0x36, 0x42, 0x3d, 0x3d, 0x49, 0x00]

ans = ""
for i in xrange(42):
    ans += chr((((data[i] & 0xAA) >> 1) | (2 * (data[i] & 0x55))) - 9)

print ans

0x08 2018-rev

这个我也不知道用的是不是预期解。

直接运行文件,提示

argc == 2018 && argv[0][0] == 1 && envp[0][0] == 1

经过调试写了一个gdb脚本绕过第一层验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
b *0x4005F0
r

#set args
set $rdi=2018
#dumpargs --force
set *((long *)0x7fffffffe332)=0x0101010101010101
set *((long *)0x7fffffffe34d)=0x0101010101010101
c

# zdump /etc/localtime
# 因为ASLR请自行修改对应地址

第二层验证提示

Bad timing, you should open this at 2018/1/1 00:00:00 (UTC) :(

刚开始的想法也是和第一层验证一样设置运行过程中各个参数的值,但后来发现这些很难找,卡了一段时间后如梦初醒,程序的运行时间是从**/etc/localtime**获取的(代码中可以看出),只要保证程序运行的瞬间**zdump /etc/localtime**的值为**2018/1/1 00:00:00 (UTC)**即可,关于如何设置时间,刚开始的想法是在gdb调试过程中set,尝试了很久,最后还是写了一个shellscript

1
2
3
4
5
6
#!/usr/bin/env bash

while true
do
	sudo date -us "2018-01-01 00:00:00"
done

这样一直运行setTime.sh,然后gdb导入gdb脚本,即可获得flag

这个题好像是文件放错了,最后的flag不完全正确,但很容易能猜出正确的flag

0x09 what-the-hell

这个题有点意思,看题目的提示好像是要优化算法,但我用了非预期解.我把思路和写残了的代码放出来,供大家参考.

用IDA打开,calc_key3的逻辑很清晰,就是运算比较麻烦根据题意应该就是要优化这里了.但看到这里的条件时我忽然想到了前一阵看到的z3,试了一下,很快就跑出来了,计算a1,a2的z3代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def getInput():
    a2 = BitVec('a2', 32)
    a1 = BitVec('a1', 32)
    #  print type(a2)
    #  print type(a1)
    
    s = Solver()
    s.add(a2 * a1 == 0xDDC34132)
    s.add(((a1 - a2) & 0xFFF) == 0xCDF)
    s.add((a1 ^ 0x7E) * (a2 + 16) == 0x732092BE)
    
    while s.check() == sat:
        if isPrime(s.model()[a1].as_long()):
            print s.model()
        s.add(Or(a1 != s.model()[a1], a2 != s.model()[a2]))
    else:
        print "unset"

跑出了符合题意的两组解:

可以看出,what函数实际上是一个求斐波那契数列的函数

但这里有一个坑点,result是int型的,要考虑溢出,经过测试,当a1=4284256177时,函数有解,返回值1095061718431,这样我们就得到了decrypt_flag的三个参数,就可以写脚本跑出flag了,脚本如下:

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'

from z3 import *
from libnum import prime_test as isPrime
from libnum import n2s

def getInput():
    a2 = BitVec('a2', 32)
    a1 = BitVec('a1', 32)
    #  print type(a2)
    #  print type(a1)
    
    s = Solver()
    s.add(a2 * a1 == 0xDDC34132)
    s.add(((a1 - a2) & 0xFFF) == 0xCDF)
    s.add((a1 ^ 0x7E) * (a2 + 16) == 0x732092BE)
    
    while s.check() == sat:
        if isPrime(s.model()[a1].as_long()):
            print s.model()
        s.add(Or(a1 != s.model()[a1], a2 != s.model()[a2]))
    else:
        print "unset"

def getKey3():
    fib = [0, 1]
    for i in xrange(2, 9999999):
        #  print i - 1, (fib[i - 1] & 0xffffffff)
        fib.append(fib[i - 1] + fib[i - 2])
        if (fib[i] & 0xffffffff) == 4284256177:
            return i * 1234567890 + 1
def getFlag():
    junk_data = [
  0x09, 0x23, 0x8C, 0xB9, 0x2F, 0x19, 0x8D, 0xF8, 0xF3, 0x79,   0x81, 0x87, 0x93, 0x99, 0x35, 0x52, 0x9C, 0xF0, 0x34, 0x99,   0x23, 0xB1, 0x84, 0x1D, 0xF0, 0x8F, 0x7E, 0x45, 0x0F, 0xCB,   0x40, 0xF8, 0x4E, 0xD1, 0x42, 0x29, 0x76, 0x17, 0x43, 0xE1,   0xAC, 0x04, 0x37, 0xA0, 0xE4, 0x30, 0x59, 0xA9, 0x68, 0xD9,   0x1C, 0x96, 0xFC, 0x1D, 0x85, 0xEA, 0xD2, 0x94, 0x07, 0x90,   0x09, 0xD2, 0xC9, 0x19, 0x86, 0xC9, 0xDC, 0x24, 0x6F, 0x3B,   0x5C, 0x92, 0x4C, 0x9F, 0xD9, 0x50, 0xDD, 0x98, 0x37, 0x1C,   0xB1, 0xDA, 0xA5, 0x44, 0xF2, 0x8E, 0x43, 0x66, 0x91, 0xA3,   0xDF, 0xAF, 0x3A, 0x7E, 0x65, 0x91, 0x19, 0x22, 0xFD, 0xFE,   0x14, 0xBA, 0x0A, 0xE1, 0xB9, 0x61, 0x73, 0x86, 0xE1, 0x96,   0xC1, 0x67, 0xCE, 0x06, 0x25, 0x74, 0xF0, 0x2E, 0xA3, 0xBB,   0xED, 0x68, 0x3E, 0x53, 0x30, 0x43, 0x0E, 0x53, 0xB8, 0x8A,   0x9C, 0x95, 0x41, 0xC3, 0xB0, 0x25, 0x1C, 0xCB, 0x38, 0x86,   0xA6, 0x7A, 0x6F, 0xF2, 0x63, 0x0A, 0x19, 0x7C, 0x07, 0xDA,   0x6F, 0xA2, 0x4E, 0xD2, 0x74, 0x4A, 0xF9, 0xAF, 0xC2, 0x9C,   0xFD, 0x89, 0xE6, 0x04, 0x11, 0xF6, 0x6F, 0xF5, 0x98, 0x55,   0x9D, 0x37, 0x12, 0xF2, 0xA6, 0x66, 0xBE, 0x85, 0x87, 0x8E,   0x87, 0x64, 0x5E, 0xA0, 0x61, 0x52, 0xD8, 0xBB, 0x39, 0x3D,   0x7B, 0xD2, 0x47, 0x27, 0x37, 0x30, 0xB5, 0xF8, 0x90, 0xFC,   0x50, 0xF3, 0xC1, 0x5C, 0x6B, 0xA4, 0xBE, 0x8D, 0xA5, 0xEA,   0xDD, 0x72, 0xF2, 0x28, 0xE1, 0x74, 0xEF, 0x07, 0x10, 0xCF,   0x39, 0x7D, 0x58, 0xE7, 0x46, 0x09, 0x04, 0xE9, 0xE9, 0x37,   0xD7, 0xE1, 0x20, 0xF9, 0xC2, 0x54, 0x28, 0xE7, 0x30, 0xE8,   0x86, 0x58, 0x77, 0x6C, 0x7D, 0x2E, 0x00, 0xCE, 0xCC, 0x9C,   0xFB, 0xA3, 0x8D, 0xD1, 0x04, 0x98, 0x9D, 0x4F, 0xE8, 0x1F,   0x60, 0x3A, 0x8A, 0x5B, 0x1A, 0x11, 0x55, 0xF0, 0x6B, 0xCF,   0xD8, 0x6D, 0x75, 0x30, 0x9A, 0xD8, 0xD8, 0x5D, 0x2E, 0x90,   0x7E, 0x43, 0x5C, 0xEB, 0x3F, 0x26, 0x78, 0xAF, 0xB3, 0xB0,   0xC3, 0x1C, 0xE9, 0xAB, 0x94, 0xE6, 0xC1, 0x49, 0x25, 0x4B,   0xAA, 0xFF, 0x59, 0xE1, 0x11, 0x48, 0x3C, 0xB9, 0x16, 0x67,   0x27, 0xF9, 0xA0, 0x29, 0x68, 0x2E, 0xFB, 0x45, 0x5D, 0x29,   0x12, 0x0A, 0x36, 0x04, 0x54, 0xB3, 0xCF, 0x87, 0x24, 0x37,   0x8E, 0x7C, 0x5A, 0xEF, 0xF8, 0x33, 0xE2, 0xE0, 0x89, 0x83,   0xA8, 0x4D, 0x72, 0x28, 0x80, 0xAA, 0xD4, 0x0E, 0xDD, 0x72,   0xA5, 0x0B, 0xAD, 0x85, 0x6F, 0xEE, 0x44, 0xAD, 0x43, 0x7D,   0x30, 0xC2, 0x15, 0xC9, 0x72, 0x12, 0x53, 0x8A, 0x37, 0x9D,   0xF2, 0x64, 0x1D, 0x21, 0x5E, 0x49, 0x78, 0x54, 0xC0, 0xF0,   0xA9, 0x81, 0xE3, 0x32, 0xD4, 0x99, 0x81, 0x88, 0x64, 0xFE,   0x20, 0x92, 0x89, 0xD0, 0xC9, 0x5A, 0xCE, 0xFA, 0xB5, 0xE4,   0x2A, 0x9D, 0x50, 0xAB, 0x32, 0x35, 0x8D, 0x31, 0x4C, 0x94,   0x6C, 0xC0, 0xEF, 0xF4, 0xE2, 0x40, 0xF7, 0x47, 0x51, 0xDB,   0x1C, 0x6D, 0x3B, 0x6B, 0xEA, 0xDA, 0x16, 0x9A, 0x27, 0x68,   0xA3, 0x73, 0xBF, 0x9D, 0x40, 0x8F, 0x07, 0xF3, 0xC7, 0x65,   0x57, 0xB7, 0x7E, 0x0C, 0xEA, 0xC9, 0x9F, 0x7F, 0x46, 0x82,   0xE6, 0x5C, 0xE6, 0xDF, 0xFE, 0x42, 0x41, 0x12, 0x62, 0x33,   0x74, 0xFF, 0xE9, 0x52, 0xD1, 0x0F, 0x75, 0x88, 0x43, 0x17,   0x02, 0x5A, 0x9E, 0x29, 0xAD, 0x40, 0x62, 0xDB, 0x1F, 0x2C,   0xE7, 0xA8, 0x6E, 0xAC, 0x62, 0xC4, 0xBE, 0xEC, 0x98, 0xB3,   0xE9, 0x44, 0xD4, 0x3E, 0xC3, 0x9E, 0x0F, 0xBC, 0xAD, 0xC6,   0x28, 0x28, 0x95, 0x93, 0xD1, 0xD5, 0x03, 0xAA, 0x78, 0xE3,   0x0D, 0x20, 0x90, 0x58, 0x82, 0xCC, 0x5F, 0x46, 0xF9, 0x2C,   0x17, 0x55, 0xDB, 0x96, 0x0A, 0x34, 0x69, 0x6B, 0x87, 0x2B,   0xB2, 0x45, 0x9D, 0x7C, 0xEA, 0xF3, 0xAB, 0x19, 0x6A, 0xE3,   0x73, 0x9D, 0x84, 0x6C, 0x3A, 0x04, 0xB5, 0x07, 0x3D, 0x10,   0x3E, 0x67, 0x5E, 0x53, 0x86, 0xDB, 0xA0, 0x39, 0xAB, 0xE0,   0x06, 0x22, 0x27, 0x8C, 0x81, 0xD3, 0xC6, 0x1F, 0x15, 0x35,   0x8D, 0x26, 0x8D, 0x67, 0x03, 0xBD, 0xC3, 0x76, 0xF7, 0x27,   0x29, 0x82, 0xAF, 0x64, 0x9D, 0x15, 0x0F, 0xBE, 0xAD, 0xAB,   0x39, 0x50, 0xD7, 0xB9, 0x1B, 0x0A, 0x3F, 0x99, 0xCC, 0x6A,   0xF5, 0xFF, 0x5B, 0xDE, 0x9F, 0xD1, 0x4D, 0xFC, 0xF4, 0x21,   0x83, 0xD0, 0x33, 0xAB, 0xA4, 0x3E, 0x3B, 0x3A, 0x67, 0x41,   0xDE, 0x93, 0xB1, 0x2A, 0xC4, 0x98, 0xDA, 0xEA, 0x51, 0xFD,   0xCF, 0xEB, 0xF0, 0xC7, 0x1F, 0xF6, 0x4C, 0xFC, 0x04, 0xEF,   0x24, 0x51, 0xBF, 0x0D, 0xB0, 0x50, 0x2C, 0x06, 0xAA, 0x0E,   0x2F, 0x74, 0xB0, 0xD5, 0x27, 0xE7, 0xD3, 0xC4, 0xA7, 0x57,   0x0D, 0x31, 0xCE, 0xD2, 0x5F, 0x6F, 0x99, 0x43, 0xDB, 0x93,   0x59, 0x24, 0xC0, 0xA8, 0x29, 0x3F, 0xDA, 0x70, 0xBD, 0x92,   0x8A, 0xC5, 0x76, 0xF9, 0x31, 0x8B, 0x98, 0xD6, 0x0A, 0x7C,   0xA0, 0x8D, 0x9B, 0x96, 0x4A, 0x77, 0x5B, 0xC2, 0xE5, 0x46,   0x72, 0x28, 0x4F, 0x54, 0x44, 0x06, 0xB4, 0xE5, 0xB2, 0x6C,   0xEF, 0x4E, 0x1E, 0x7E, 0xAE, 0x0A, 0xC0, 0x7D, 0x1E, 0x6E,   0x80, 0x3A, 0xDF, 0x88, 0x07, 0x4B, 0xF8, 0xCE, 0x3A, 0x40,   0x60, 0x6F, 0xDA, 0x9F, 0xE4, 0xD9, 0x58, 0xA3, 0x19, 0xEC,   0x5A, 0xD8, 0x85, 0x52, 0x1E, 0xA8, 0xCA, 0x04, 0xDC, 0x5D,   0xD2, 0x77, 0x45, 0x35, 0xB0, 0x5A, 0xD1, 0xCD, 0xDC, 0x30,   0xA6, 0x14, 0xA6, 0xA1, 0xBF, 0x24, 0xE1, 0xDE, 0xE6, 0xEF,   0xA9, 0x0E, 0x00, 0x64, 0x5D, 0xEF, 0x11, 0x4A, 0xF3, 0x38,   0x52, 0x86, 0x81, 0x6F, 0x42, 0xFB, 0x8B, 0x4B, 0x36, 0xFB,   0x79, 0x9D, 0x82, 0xBC, 0x0D, 0x01, 0x14, 0x42, 0x86, 0xD7,   0x65, 0xB4, 0x51, 0xBF, 0xEC, 0x64, 0xE4, 0x61, 0x21, 0x63,   0x99, 0xD3, 0xC5, 0xFE, 0x58, 0x0A, 0xF5, 0xA1, 0xD5, 0xB0,   0xD9, 0xB4, 0x8A, 0x02, 0xC7, 0x50, 0xDE, 0xDE, 0xF2, 0xBE,   0x13, 0xF8, 0x3F, 0x23, 0x51, 0x4C, 0x19, 0x40, 0x74, 0xA6,   0x35, 0xBA, 0x4B, 0x71, 0x1B, 0xAE, 0xFE, 0x43, 0x8F, 0xA4,   0x25, 0xA5, 0xE5, 0x31, 0xB3, 0x17, 0x00, 0x83, 0x34, 0x4A,   0xBA, 0x05, 0xCF, 0xBB, 0xB8, 0x67, 0x25, 0xE0, 0xD3, 0x53,   0xFC, 0xAA, 0xBA, 0xB3, 0x6C, 0x8A, 0xEC, 0x8F, 0x9C, 0xDB,   0x47, 0x05, 0x8E, 0x5A, 0x3E, 0xD4, 0x7B, 0x5F, 0xC5, 0x42,   0xD1, 0x6C, 0x2C, 0x99, 0xBA, 0xFD, 0x9D, 0x6B, 0x52, 0xD2,   0x34, 0x86, 0x6A, 0x5D, 0x5E, 0x50, 0xB3, 0x58, 0xD4, 0x3A,   0xB7, 0x12, 0x46, 0x0E, 0x40, 0x81, 0xA5, 0x21, 0x5D, 0x5E,   0x63, 0xE5, 0x3B, 0x30, 0x3B, 0x6E, 0x13, 0x73, 0x36, 0x20,   0x3C, 0xE3, 0xA9, 0x99, 0x70, 0x49, 0x92, 0xFC, 0xFA, 0x70,   0x24, 0x6F, 0x7B, 0x1D, 0x93, 0x8D, 0x7D, 0xB4, 0xAE, 0x2A,   0x7D, 0x53, 0x5C, 0x68, 0xEA, 0xFA, 0x94, 0x58, 0x54, 0x28,   0xCF, 0x23, 0xFB, 0x70, 0x80, 0x7F, 0xF0, 0x4F, 0x2A, 0x0B,   0x94, 0xD7, 0x3E, 0x7F, 0x78, 0x45, 0xFC, 0xE3, 0xA9, 0x3E,   0x1E, 0x23, 0xA3, 0x7E, 0x06, 0x00, 0x1D, 0x66, 0x50, 0x9D,   0xD1, 0x1F, 0x65, 0x7E, 0x76, 0x8F, 0x47, 0x73, 0xF0, 0xAA,   0x3A, 0xC5, 0xB8, 0xB0, 0x65, 0xDD, 0x34, 0x48, 0x80, 0x30,   0x46, 0xE0, 0x0A, 0xDD, 0x1B, 0xC6, 0xD6, 0x88, 0xFB, 0x76,   0x0A, 0xA5, 0xE9, 0xB5, 0xC8, 0xBC, 0x0B, 0x82, 0x1C, 0x33,   0xA3, 0x4D, 0xD3, 0xCE, 0x2F, 0x2A, 0x8E, 0xFA, 0xAA, 0xB2,   0x5D, 0x57, 0x89, 0x03, 0x56, 0x5F, 0xF2, 0x05, 0xF7, 0x24,   0xE6, 0xB6, 0x13, 0x84, 0xBC, 0x5D, 0xA5, 0x8F, 0x0D, 0xAC,   0xC1, 0xA7, 0xDB, 0x2A, 0xDF, 0xB9, 0xDA, 0x91, 0xFB, 0xF1,   0xD7, 0x83, 0x36, 0xCC, 0x3D, 0xBE, 0x14, 0xEF, 0x51, 0x57,   0xE1, 0xBF, 0x6A, 0x3F, 0x5F, 0xEA, 0xA8, 0x08, 0xB6, 0x83,   0x84, 0xA2, 0x8B, 0x2F, 0x13, 0x2B, 0x59, 0x9D, 0x86, 0x29,   0x22, 0x53, 0x17, 0xEE, 0x15, 0x84, 0x3B, 0x1E, 0x2D, 0x10,   0xF0, 0x8B, 0xC3, 0xAD, 0x4B, 0x45, 0x50, 0x06, 0x12, 0xAA,   0x94, 0x60, 0x07, 0x09, 0x6B, 0x2A, 0xDA, 0xBF, 0x86, 0x90,   0x9A, 0xFB, 0xAF, 0xEC, 0xBE, 0x05, 0x4A, 0x1E, 0xFC, 0x6E,   0xFE, 0x81, 0xC0, 0x1B, 0xB2, 0x39, 0x2F, 0x5C, 0x05, 0x40,   0xAB, 0x0B, 0x4E, 0xE3, 0x69, 0x15, 0x9A, 0x3F, 0x70, 0x94,   0x10, 0xE7, 0x91, 0xEF, 0x1E, 0x69, 0xE3, 0x6D, 0xF6, 0x43,   0xE5, 0xEB, 0xE4, 0x1E, 0xFE, 0xAC, 0xAF, 0x64, 0xCD, 0xF0,   0x59, 0x07, 0x0B, 0x5A, 0xC8, 0xED, 0x19, 0x84, 0xD0, 0x4D,   0xF8, 0xCC, 0x85, 0x54, 0x75, 0xFC, 0xE8, 0x6E, 0x3F, 0x5E,   0xF8, 0xB6, 0x39, 0xDC, 0x7F, 0x24, 0x7D, 0x7E, 0x83, 0x3F,   0xF4, 0xB9, 0x8A, 0xE8, 0xC8, 0xDC, 0x7A, 0xFB, 0x2E, 0x63,   0xB2, 0x5C, 0x11, 0xF6, 0x8B, 0xFB, 0x83, 0x20, 0xBA, 0x00,   0x9A, 0x04, 0xFB, 0xD5, 0xDD, 0x51, 0x8D, 0x90, 0x59, 0x0C,   0xF9, 0xA5, 0xEF, 0x24, 0x98, 0x09, 0x26, 0x97, 0x32, 0x97,   0xBC, 0xAA, 0x3D, 0x82, 0x9E, 0xB0, 0x2D, 0xA8, 0x23, 0x76,   0xCE, 0x5C, 0x59, 0x2A, 0x9F, 0x12, 0xAA, 0x60, 0x8D, 0x8F,   0x1F, 0xD1, 0xE1, 0x67, 0xC8, 0x2A, 0x19, 0x67, 0x66, 0x5B,   0x81, 0xAE, 0x98, 0xD3, 0x3A, 0x17, 0xF5, 0xAA, 0xD6, 0x43,   0xF7, 0xD9, 0x6A, 0xA4, 0x71, 0x08, 0xFC, 0xFE, 0x5F, 0x01,   0xA4, 0x26, 0x06, 0x94, 0x95, 0xBB, 0xBE, 0x0A, 0xCF, 0x2D,   0x88, 0x1F, 0x7E, 0xBF, 0x21, 0x12, 0x51, 0x1E, 0xBC, 0xB9,   0xA3, 0x56, 0x20, 0x9C, 0x60, 0x82, 0x57, 0x41, 0x82, 0xCC,   0x91, 0xC8, 0xFF, 0xEF, 0xCD, 0xCF, 0x61, 0x17, 0xE5, 0xE9,   0x55, 0xA0, 0xFD, 0xD4, 0x12, 0x1B, 0x5C, 0xCA, 0x75, 0x73,   0x19, 0x87, 0xD6, 0xD6, 0x08, 0x29, 0xEE, 0xA9, 0x96, 0xFE,   0x7F, 0x6A, 0xBA, 0x68, 0xE9, 0x88, 0x3F, 0xD7, 0x6B, 0xE1,   0x9C, 0x26, 0x45, 0x39, 0x28, 0x5B, 0xC1, 0xED, 0x40, 0xF3,   0x1C, 0x1E, 0x05, 0xC3, 0x69, 0x29, 0x7A, 0xF1, 0x48, 0xDA,   0xB3, 0xB3, 0xF0, 0x86, 0xC9, 0xCE, 0xDD, 0x29, 0xDA, 0x53,   0xF5, 0x47, 0x1A, 0x11, 0x5E, 0x07, 0x5A, 0x94, 0x7C, 0x72,   0x21, 0x71, 0x63, 0xAE, 0xB3, 0xEC, 0x17, 0xA8, 0xC4, 0xDB,   0x13, 0x61, 0x58, 0xA4, 0x6C, 0x63, 0x0A, 0xA6, 0xD5, 0xC5,   0xFF, 0x0E, 0xC3, 0x3B, 0xCB, 0xA2, 0x56, 0x04, 0x86, 0x32,   0x71, 0xBF, 0xD9, 0xE5, 0xED, 0x01, 0x52, 0xC8, 0xD3, 0x2D,   0x08, 0xF9, 0x6B, 0xF0, 0x53, 0x71, 0x23, 0x07, 0xA7, 0xDD,   0xA1, 0xA1, 0x39, 0xA8, 0x27, 0x7C, 0xAD, 0xCE, 0xBA, 0x46,   0xDE, 0xEF, 0x5C, 0x8C, 0x98, 0xBE, 0xDA, 0xAE, 0x63, 0xF6,   0xDF, 0x4C, 0x7F, 0x29, 0x83, 0x65, 0x87, 0x09, 0x2B, 0xFB,   0x10, 0xC1, 0xDB, 0xFF, 0x08, 0x2A, 0x9D, 0x87, 0x29, 0x86,   0x34, 0x0E, 0xA3, 0x43, 0x29, 0x46, 0x33, 0xF0, 0x6C, 0x53,   0x20, 0x89, 0x36, 0x49, 0x7E, 0x5B, 0x11, 0x80, 0xA6, 0x48,   0x80, 0xB9, 0xB9, 0x32, 0xB3, 0xC8, 0x16, 0xD2, 0x05, 0x47,   0x53, 0xB5, 0x96, 0x15, 0x82, 0x16, 0x3B, 0x25, 0x47, 0x53,   0x3E, 0x95, 0xEA, 0xAD, 0x9D, 0x91, 0x94, 0xF9, 0xD4, 0x8B,   0x53, 0x66, 0xAE, 0x8C, 0x0E, 0x1F, 0xB0, 0xCF, 0xA4, 0x3E,   0x91, 0x9A, 0xE3, 0xDE, 0xB5, 0xB0, 0xDA, 0xF2, 0x9D, 0x0C,   0x79, 0xB6, 0x7B, 0x70, 0x0A, 0x50, 0x4F, 0x7A, 0x58, 0x33,   0x89, 0x74, 0x9D, 0xA7, 0xAD, 0x71, 0x9C, 0xD0, 0x8F, 0xCA,   0xC9, 0x51, 0xC7, 0x81, 0x0C, 0xC6, 0x6A, 0x8A, 0x52, 0xB6,   0x0C, 0xD9, 0x86, 0x92, 0x33, 0xDC, 0x9E, 0xA9, 0xE7, 0xF2,   0xED, 0xA5, 0x4A, 0x80, 0x5F, 0x00, 0xB7, 0xDB, 0x75, 0xA9,   0x81, 0x12, 0x56, 0xC7, 0xE8, 0x72, 0xCB, 0xC9, 0x62, 0x38,   0x03, 0x76, 0xB2, 0x57, 0xCD, 0x1A, 0xF7, 0xFF, 0x3C, 0x1D,   0x5F, 0xB4, 0x4C, 0x90, 0x3E, 0x8D, 0x10, 0x7E, 0x33, 0xFD,   0x59, 0xD9, 0xAD, 0xF5, 0x33, 0x58, 0x41, 0xFF, 0xDA, 0x8E,   0x06, 0x37, 0x52, 0x9E, 0x68, 0xFC, 0xCC, 0x59, 0xAA, 0x27,   0x11, 0x34, 0x02, 0x63, 0x00, 0x03, 0x06, 0x60, 0x90, 0xDE,   0x07, 0xD9, 0x15, 0x8A, 0x71, 0x03, 0x6C, 0x6F, 0x4A, 0x56,   0x8F, 0x08, 0x7F, 0x63, 0xE0, 0xA9, 0x23, 0x5B, 0x27, 0xE8,   0xD7, 0xC0, 0x8E, 0xD6, 0xA0, 0x6F, 0xB5, 0x1D, 0x96, 0x39,   0x21, 0x76, 0x3C, 0x74, 0xDC, 0xA2, 0xC9, 0x3A, 0xCC, 0x1B,   0x67, 0x06, 0x1C, 0xF6, 0x48, 0xF4, 0x57, 0x31, 0x48, 0xF2,   0x07, 0xD7, 0xCF, 0xF7, 0x63, 0x50, 0xC0, 0x03, 0x15, 0x2E,   0xA0, 0x26, 0x48, 0xA6, 0x2F, 0x3F, 0xD2, 0x96, 0x0A, 0xEE,   0x52, 0x1F, 0xBF, 0x1A, 0x0F, 0xB8, 0xAF, 0x32, 0xBC, 0x78,   0x46, 0x43, 0x36, 0x28, 0x30, 0x4E, 0x7B, 0x57, 0x0D, 0x58,   0xB5, 0xB6, 0x2E, 0x3D, 0x9B, 0x32, 0xCA, 0x1C, 0x69, 0x74,   0x42, 0x13, 0xD3, 0x4F, 0x64, 0x2E, 0x1B, 0x65, 0xBB, 0x0A,   0x1F, 0xAA, 0xD9, 0x5E, 0xBD, 0x2F, 0xA0, 0xD3, 0xA8, 0xEF,   0x1B, 0xAC, 0xF8, 0x42, 0x96, 0x6F, 0xC3, 0x44, 0x6E, 0x2F,   0x97, 0x36, 0x9A, 0x18, 0x1E, 0x0D, 0xB9, 0xA0, 0x29, 0x5D,   0xCB, 0xD4, 0xE2, 0xBE, 0x55, 0x59, 0xA6, 0x26, 0x9E, 0x57,   0xAA, 0x62, 0xEB, 0xC0, 0x6D, 0x76, 0x45, 0x80, 0xC4, 0xDF,   0x91, 0x39, 0x32, 0xE9, 0xC3, 0xFD, 0x94, 0x49, 0xCE, 0x8C,   0x98, 0xDE, 0x4A, 0x6D, 0x6E, 0x60, 0xE7, 0x8D, 0x89, 0x95,   0x26, 0x8B, 0x79, 0x53, 0xBC, 0xB2, 0xFB, 0xC6, 0x9C, 0x9B,   0x99, 0xE2, 0x99, 0xE2, 0xAE, 0xB8, 0x94, 0x62, 0x9B, 0x3F,   0x41, 0x5F, 0xC9, 0x4E, 0x64, 0xDC, 0x93, 0xFA, 0xB8, 0x0B,   0x9E, 0x6E, 0x2F, 0x92, 0xD9, 0xDB, 0xCF, 0xFA, 0x85, 0x9F,   0x0E, 0xB0, 0x54, 0x72, 0x4A, 0x3D, 0xFA, 0x48, 0x10, 0xFE,   0x14, 0x4D, 0x6F, 0xA2, 0x65, 0x80, 0xF1, 0x86, 0xE3, 0x37,   0x28, 0x6B, 0x7D, 0x7F, 0xF0, 0x62, 0xCF, 0x8E, 0x66, 0x3E,   0xE3, 0x65, 0xDD, 0x26, 0xDE, 0xA4, 0x0D, 0x6D, 0x26, 0x1C,   0x5D, 0x69, 0x70, 0xBE, 0x99, 0xE2, 0xD1, 0xDB, 0xDE, 0xC2,   0x90, 0xF5, 0xB1, 0x69, 0x2E, 0x75, 0x3C, 0xB1, 0xA5, 0x93,   0xF8, 0x01, 0x40, 0xE7, 0x39, 0x42, 0x0C, 0x39, 0xE0, 0xED,   0x97, 0xC3, 0xBA, 0x89, 0x77, 0xC3, 0xB6, 0x5E, 0xA8, 0x40,   0xF6, 0x8F, 0x32, 0xB3, 0x23, 0x9E, 0x92, 0xDB, 0x10, 0xB2,   0xD0, 0xFD, 0xB4, 0x32, 0x2E, 0xB3, 0xC6, 0x24, 0x6F, 0xCE,   0x01, 0xCE, 0x27, 0xD8, 0x5C, 0x7D, 0xA5, 0x1F, 0xCC, 0x48,   0x53, 0x07, 0x8F, 0x8B, 0x53, 0xAD, 0x94, 0xBA, 0xE7, 0x62,   0xEB, 0x53, 0xEA, 0xEC, 0xA0, 0x05, 0x94, 0x0C, 0xD4, 0x72,   0x6D, 0x24, 0x50, 0xC1, 0x85, 0xA3, 0xBB, 0x51, 0x52, 0x13,   0xCF, 0xF3, 0x39, 0x3F, 0x5B, 0x5A, 0x6D, 0xBD, 0xB6, 0x9B,   0xAE, 0x4C, 0x60, 0x1A, 0x9C, 0x48, 0x40, 0x6E, 0x0A, 0xC5,   0x96, 0x25, 0xCE, 0x0A, 0x26, 0x9A, 0x0E, 0x47, 0xAD, 0xC8,   0x43, 0x0C, 0xD7, 0xF8, 0xB7, 0x5B, 0xAA, 0x3B, 0x16, 0xBF,   0x8A, 0xFF, 0x7B, 0x0F, 0xF3, 0x5F, 0x0B, 0x4D, 0x62, 0xE1,   0x3C, 0x5E, 0xE0, 0x70, 0xB6, 0x31, 0xF9, 0xBF, 0xC3, 0x77,   0xDE, 0xB6, 0x17, 0xF6, 0x0E, 0x53, 0x32, 0x3E, 0x3F, 0x93,   0x73, 0xE7, 0x72, 0xCE, 0x8D, 0xC3, 0xFE, 0x89, 0xEF, 0xD7,   0xCA, 0xEA, 0x85, 0xB2, 0xF0, 0xF2, 0xB8, 0x7B, 0x46, 0xB7,   0x71, 0x98, 0x79, 0x8B, 0xAC, 0x0B, 0xDA, 0x4C, 0x86, 0x7B,   0x42, 0x53, 0x69, 0x05, 0x6B, 0xDA, 0x34, 0x4B, 0xB3, 0xB2,   0x49, 0x2D, 0x9D, 0xAB, 0xB9, 0xC8, 0x2B, 0x3F, 0xB3, 0x9D,   0x66, 0x71, 0xD0, 0x9F, 0xFC, 0x4E, 0xF0, 0xCE, 0x4F, 0xAC,   0x4E, 0x08, 0x2A, 0x23, 0xDE, 0xA2, 0x1F, 0x2F, 0x21, 0xCE,   0x73, 0x42, 0xB6, 0xF2, 0xEF, 0x4B, 0x6E, 0x56, 0xF6, 0x35,   0xAD, 0x2D, 0x61, 0x7D, 0x44, 0xCB, 0x61, 0x08, 0xAF, 0xD3,   0x94, 0x12, 0x7B, 0x61, 0x48, 0xDE, 0x2E, 0xB8, 0x98, 0xF8,   0xC3, 0x66, 0xBC, 0x27, 0x31, 0x77, 0x33, 0x9F, 0xB7, 0x68,   0x39, 0xB8, 0x7C, 0x16, 0x76, 0x68, 0xA9, 0x58, 0x08, 0xE6,   0x07, 0xFB, 0xBD, 0xEF, 0x27, 0xCD, 0x47, 0x71, 0xCD, 0xCB,   0x81, 0x48, 0x4B, 0xCC, 0xA5, 0x85, 0xD2, 0xDA, 0xC9, 0x8C,   0x5C, 0x68, 0xC3, 0xA6, 0x83, 0x98, 0x6B, 0xEE, 0x51, 0x8A,   0x65, 0x32, 0x94, 0x27, 0x11, 0x2C, 0x6D, 0xA3, 0x6A, 0xD3,   0xF6, 0xD5, 0xBB, 0x27, 0xBA, 0x54, 0x1C, 0x92, 0xF7, 0xBF,   0x17, 0x7F, 0x7A, 0xA7, 0x01, 0x8B, 0x84, 0x56, 0x46, 0x13,   0xCF, 0x18, 0xD1, 0x60, 0xC8, 0x08, 0xE0, 0x3C, 0x63, 0x2F,   0x4F, 0xFA, 0xE8, 0x5C, 0x3B, 0xAD, 0x5C, 0x45, 0x62, 0x3A,   0xD1, 0xBE, 0x75, 0x3D, 0x79, 0x26, 0xF0, 0xA2, 0x82, 0x23,   0xB8, 0x8C, 0xFE, 0xC7, 0x2A, 0x38, 0x03, 0xC1, 0x6D, 0x87,   0xFD, 0xBA, 0x28, 0x55, 0x22, 0xE7, 0x4F, 0xB4, 0x33, 0xB7,   0x7D, 0x88, 0xAE, 0x79, 0x4F, 0x87, 0x0F, 0xE3, 0x26, 0xD2,   0xE7, 0x4E, 0xC8, 0x69, 0xAB, 0x8A, 0x15, 0x19, 0x95, 0xC3,   0x0D, 0x57, 0xD3, 0x5B, 0x67, 0x24, 0x10, 0x31, 0x35, 0x23,   0xA5, 0xDF, 0x0B, 0xC7, 0xD3, 0x20, 0x11, 0x8B, 0xB3, 0x09,   0xD3, 0x3C, 0x6B, 0x25, 0x80, 0xAE, 0xCD, 0x50, 0x32, 0x19,   0xC0, 0x09, 0xA8, 0x52, 0x93, 0x0A, 0x78, 0x8F, 0x01, 0x0A,   0xD2, 0x24, 0x96, 0x52, 0x06, 0x2A, 0xBD, 0xD5, 0x71, 0x42,   0x5D, 0xB5, 0x23, 0x22, 0xBA, 0xA5, 0x17, 0xAB, 0xA0, 0xE3,   0x2B, 0xB5, 0x34, 0xCC, 0x83, 0x98, 0xAC, 0x23, 0x92, 0xE7,   0x7F, 0x3B, 0x6B, 0x8A, 0x29, 0x8F, 0x44, 0x6D, 0x07, 0x67,   0xA7, 0xAA, 0x1B, 0x37, 0xE1, 0x2B, 0xE5, 0x39, 0x7E, 0x42,   0xEB, 0xFA, 0x2C, 0x09, 0x1D, 0x77, 0x95, 0xAB, 0x3A, 0x41,   0x4B, 0xD2, 0x73, 0xAF, 0xE2, 0xC8, 0xA3, 0xEA, 0xFE, 0xAE,   0x69, 0x75, 0x3F, 0x54, 0x93, 0x40, 0x13, 0x7A, 0xC8, 0xEA,   0x3B, 0x85, 0xD1, 0x82, 0xDD, 0x6B, 0x93, 0xAD, 0xB1, 0x9A,   0xD5, 0x33, 0x8B, 0xD9, 0x3F, 0x40, 0x8E, 0x4E, 0xCA, 0xF1,   0x74, 0x58, 0xFC, 0xD7, 0xA7, 0xC7, 0xBF, 0x6D, 0x61, 0x13,   0x9F, 0x64, 0x91, 0x1E, 0xC4, 0x00, 0x36, 0x3B, 0xB5, 0x66,   0xCF, 0xD6, 0xD0, 0x85, 0x1B, 0xDB, 0xB7, 0x94, 0x8F, 0xAF,   0x08, 0x2D, 0x28, 0xBD, 0xF9, 0x8C, 0x60, 0x6C, 0xC9, 0x93,   0x10, 0x0F, 0x0E, 0x73, 0x99, 0xFD, 0xDA, 0x7E, 0xE0, 0xA1,   0xC4, 0xE8, 0xE6, 0x19, 0x65, 0x80, 0x98, 0xB3, 0xA7, 0xC1,   0x8E, 0x2C, 0xA4, 0x2B, 0xC5, 0xAB, 0x6E, 0xAD, 0x3B, 0xF5,   0xA6, 0xC1, 0x6D, 0x1D, 0x1A, 0xBD, 0x3E, 0xB8, 0xE5, 0xAA,   0x9A, 0x7D, 0xD4, 0x56, 0x0E, 0x12, 0x33, 0x8E, 0xBF, 0x18,   0x5B, 0x4B, 0x17, 0x66, 0x76, 0x3E, 0x01, 0xC7, 0x73, 0x07,   0xF8, 0x40, 0xD6, 0x93, 0x97, 0xB5, 0x31, 0x25, 0xD1, 0xAA,   0x00, 0xF9, 0x3C, 0x42, 0x93, 0x77, 0x54, 0x11, 0x54, 0x71,   0x2E, 0x09, 0x77, 0xE1, 0x10, 0x58, 0x53, 0xCF, 0xB3, 0xD2,   0xB2, 0x72, 0x60, 0x89, 0x18, 0xAD, 0xFC, 0x09, 0xF5, 0xBC,   0x68, 0x01, 0xC2, 0xF9, 0x35, 0xE3, 0x7E, 0xB7, 0x5C, 0xE5,   0x3B, 0x9D, 0x01, 0x8C, 0xD5, 0x6B, 0x91, 0xEA, 0x9F, 0x51,   0x29, 0xD6, 0xCD, 0x2E, 0x67, 0xE8, 0x19, 0x49, 0x27, 0xEE,   0x12, 0xFC, 0x2F, 0x46, 0x0E, 0xF9, 0xCA, 0x35, 0x54, 0x67,   0x08, 0xB6, 0xED, 0x06, 0x25, 0xFF, 0x28, 0x7E, 0xCA, 0x4D,   0xBD, 0x8C, 0x76, 0x7D, 0x23, 0x8D, 0xF4, 0xAF, 0x77, 0x6C,   0x46, 0x21, 0x64, 0xF2, 0x5F, 0x7A, 0x51, 0xA5, 0xCD, 0x87,   0xA8, 0xF4, 0x63, 0x81, 0x17, 0xDB, 0x21, 0x34, 0x8E, 0x3D,   0xB1, 0xDB, 0x96, 0x25, 0xFF, 0xCE, 0xAE, 0x7D, 0xB5, 0xB8,   0x01, 0x90, 0xF4, 0x07, 0xCB, 0xFA, 0x50, 0xDB, 0xA8, 0xE3,   0xC9, 0x3F, 0xB4, 0x98, 0x53, 0xFE, 0x43, 0x8F, 0x2C, 0x9D,   0xB9, 0xF3, 0x92, 0x5D, 0x86, 0x3F, 0x8B, 0x82, 0xD0, 0x97,   0x32, 0xBF, 0x23, 0x86, 0xEC, 0x3C, 0xF3, 0x56, 0x29, 0xD5,   0x5C, 0xEB, 0x50, 0x39, 0xB8, 0x88, 0x97, 0x70, 0xE3, 0xE0,   0xDA, 0x3E, 0x61, 0x03, 0x1F, 0xC4, 0x26, 0x07, 0x6F, 0x00,   0x18, 0x89, 0x29, 0x0F, 0xF4, 0x08, 0xFD, 0x84, 0xBA, 0x52,   0xF6, 0xAB, 0x4A, 0xDF, 0x50, 0x6D, 0xB0, 0x5E, 0x5C, 0x6F,   0xD8, 0xB6, 0x0A, 0x9A, 0x42, 0x25, 0x75, 0xB2, 0x5E, 0x7C,   0x6A, 0x21, 0xD4, 0x63, 0xF0, 0xC6, 0xA1, 0x02, 0xEC, 0x28,   0x1E, 0xCC, 0x73, 0x71, 0x75, 0xD5, 0x0F, 0x4F, 0xE1, 0xE4,   0x11, 0x24, 0x6B, 0x79, 0x7D, 0x12, 0xC7, 0xB3, 0xED, 0xED,   0x93, 0x98, 0x63, 0xFF, 0x34, 0x6E, 0xFC, 0x36, 0x43, 0x83,   0x62, 0x9A, 0x64, 0x0A, 0xF3, 0x94, 0xE1, 0xC5, 0x00, 0xCA,   0x01, 0x4B, 0xCE, 0x3F, 0x48, 0xB7, 0x57, 0x69, 0x87, 0x9A,   0x82, 0xC8, 0xC4, 0xA8, 0xAD, 0x2E, 0x68, 0xBF, 0x1E, 0x85,   0xB1, 0x83, 0x4F, 0x1D, 0x39, 0x8A, 0x36, 0x04, 0xDD, 0xDB,   0x06, 0x2F, 0xFA, 0xF6, 0xF7, 0xEC, 0x7C, 0x16, 0x22, 0x17,   0x7B, 0x12, 0x28, 0xAA, 0xD8, 0x78, 0xE2, 0xF3, 0x23, 0x83,   0x1B, 0x6C, 0xCC, 0xD6, 0x3D, 0xA0, 0x99, 0x22, 0x3A, 0x85,   0xA8, 0x84, 0xD1, 0xBA, 0x26, 0x1D, 0x70, 0x01, 0x34, 0x94,   0x3D, 0x1F, 0x0C, 0xC5, 0x12, 0xD8, 0xCC, 0x55, 0x74, 0xBE,   0xB3, 0xC3, 0x4B, 0xE5, 0x45, 0x3A, 0x46, 0x17, 0x2E, 0x5F,   0x43, 0xE5, 0x0F, 0x29, 0xA6, 0x39, 0x04, 0x5E, 0xEA, 0x07,   0x1F, 0x10, 0xBB, 0x77, 0xB1, 0xD2, 0xB7, 0xBF, 0xDA, 0x30,   0x3B, 0x7C, 0x14, 0x9E, 0x22, 0xA6, 0x29, 0xBD, 0xF2, 0xB4,   0xBF, 0xCC, 0x13, 0x79, 0xB2, 0xE7, 0xA0, 0x3C, 0x81, 0x33,   0xE1, 0xB8, 0x40, 0x95, 0x5B, 0xCD, 0x6E, 0x1E, 0xDB, 0x7E,   0x52, 0x77, 0xD1, 0xBC, 0x80, 0x31, 0x40, 0x86, 0x7A, 0xD7,   0xB6, 0x5B, 0x87, 0xE6, 0xE3, 0xC5, 0xBD, 0x30, 0x6B, 0x2E,   0xFA, 0x19, 0x7D, 0x41, 0xF1, 0x73, 0x90, 0xE6, 0x53, 0x58,   0x1A, 0x88, 0x48, 0x9A, 0x83, 0x83, 0x81, 0x25, 0xEE, 0xDC,   0xDD, 0x11, 0xCD, 0x22, 0x66, 0x41, 0x84, 0x27, 0x65, 0xC6,   0x75, 0x8F, 0x78, 0x98, 0x36, 0x31, 0x30, 0x1B, 0xB4, 0xBD,   0x4B, 0xC1, 0x23, 0x73, 0x93, 0x00, 0x91, 0x8A, 0xD1, 0x39,   0x98, 0x27, 0x77, 0xC0, 0xFA, 0x21, 0x15, 0x17, 0xB3, 0xD6,   0x89, 0xDB, 0x7C, 0xE2, 0xEA, 0x7A, 0x2B, 0xAE, 0xA4, 0x1D,   0x24, 0x17, 0xD3, 0xD5, 0x4F, 0xEC, 0x3C, 0x9B, 0x06, 0xA1,   0xFD, 0xD6, 0xCD, 0xAD, 0x37, 0x95, 0xFA, 0x23, 0x77, 0x54,   0x64, 0x7C, 0x2F, 0x95, 0x02, 0x26, 0x6A, 0x4A, 0xAA, 0xFC,   0xE4, 0xF9, 0x49, 0xCA, 0x27, 0xFD, 0xFF, 0x10, 0xE2, 0xE1,   0xB4, 0xD9, 0x50, 0xC2, 0xC4, 0x89, 0xD6, 0x5C, 0x44, 0x68,   0xEA, 0xD3, 0xBB, 0x4A, 0xD6, 0x33, 0x3E, 0x42, 0xB3, 0x23,   0x69, 0x05, 0x2A, 0x9B, 0x1D, 0xDC, 0x81, 0x1C, 0xA9, 0x8A,   0x47, 0x2F, 0x84, 0x3D, 0x4E, 0x84, 0x72, 0x50, 0xAF, 0x23,   0xF3, 0x63, 0xCE, 0x26, 0xB3, 0xD6, 0xFF, 0xB7, 0x9D, 0x16,   0x8D, 0x5C, 0x6D, 0xF7, 0x5C, 0x6E, 0x7B, 0x1D, 0x8E, 0x26,   0xC0, 0xFE, 0x8C, 0x2D, 0x8E, 0x5F, 0xC5, 0xA0, 0x90, 0xCE,   0xF5, 0xA4, 0x08, 0x06, 0x0A, 0x9F, 0x34, 0xAC, 0xDA, 0xA0,   0xC7, 0x71, 0x2E, 0x12, 0x98, 0x00, 0x5C, 0x40, 0xDD, 0x1A,   0xE2, 0xC2, 0x59, 0x56, 0xF3, 0x5E, 0xE8, 0x64, 0x6F, 0x0D,   0xA2, 0xD5, 0x21, 0x50, 0x9C, 0x8B, 0x54, 0x88, 0x01, 0xA6,   0xA0, 0x58, 0x55, 0xF9, 0x57, 0xD2, 0x63, 0x13, 0x43, 0x97,   0xC3, 0x8A, 0xC1, 0xC8, 0xA2, 0xCC, 0xCE, 0xC7, 0x8E, 0xBF,   0x1F, 0x58, 0x8F, 0x2A, 0x19, 0xBC, 0x6E, 0x07, 0x91, 0x50,   0x23, 0xEB, 0x25, 0xBC, 0x90, 0xCB, 0x88, 0x8A, 0xA2, 0x06,   0x8C, 0xC6, 0x30, 0xC7, 0xCC, 0x04, 0x93, 0xF6, 0xB4, 0x74,   0x52, 0x76, 0x86, 0x79, 0xC5, 0x60, 0x98, 0xDD, 0x29, 0x46,   0x4A, 0x4B, 0x10, 0x1D, 0x35, 0x81, 0xE7, 0x59, 0xE8, 0xA1,   0x90, 0xDD, 0x75, 0x5C, 0x36, 0xB1, 0x51, 0x22, 0xE2, 0xF7,   0xF8, 0xE8, 0xDB, 0xD9, 0x4A, 0xAD, 0x08, 0xD9, 0x35, 0xF8,   0x00, 0xC4, 0x34, 0x39, 0x03, 0xC8, 0x37, 0xC5, 0x60, 0x3D,   0x25, 0x7E, 0x07, 0xBE, 0x25, 0x27, 0xB7, 0x86, 0x3A, 0x3A,   0x8C, 0xB2, 0xC1, 0xD4, 0x4E, 0xA9, 0x68, 0x15, 0x55, 0xB8,   0xBD, 0xBA, 0xFF, 0x0F, 0xD3, 0x63, 0x63, 0x9E, 0xED, 0x1E,   0x48, 0xAB, 0x18, 0xEA, 0x7D, 0xAD, 0x38, 0xD2, 0xE9, 0x77,   0x1B, 0x4B, 0xDD, 0xD9, 0x78, 0x3C, 0x27, 0x47, 0xFD, 0x02,   0xAD, 0xFE, 0x38, 0x45, 0xB5, 0xA0, 0xCC, 0x2A, 0xBD, 0xAD,   0x5B, 0x53, 0xFD, 0xA5, 0x50, 0x42, 0x5D, 0x60, 0xE9, 0x51,   0x2C, 0x4A, 0x8D, 0x58, 0xD2, 0x2B, 0x41, 0x95, 0x69, 0x3C,   0xDD, 0xD1, 0xAA, 0x9F, 0xBA, 0x41, 0x72, 0x40, 0x27, 0xC1,   0x7E, 0x38, 0xEE, 0x51, 0xC2, 0x06, 0x61, 0x14, 0x3A, 0xC4,   0xBC, 0x4C, 0x58, 0x23, 0x42, 0xC0, 0x6E, 0x70, 0x2E, 0x36,   0x2F, 0x8E, 0xD5, 0x3E, 0x9B, 0x57, 0x7F, 0x7D, 0xD7, 0x1D,   0x6D, 0x56, 0x1B, 0x52, 0xE2, 0x5B, 0xF8, 0x99, 0xCE, 0xAE,   0xD8, 0x51, 0xFB, 0xA0, 0xB8, 0xA1, 0xE7, 0x03, 0x45, 0x0F,   0xCF, 0xEA, 0xE8, 0x8A, 0x15, 0xAC, 0x59, 0xC3, 0x91, 0x49,   0x7C, 0x83, 0xB0, 0x13, 0x43, 0x51, 0x49, 0x2C, 0xE4, 0x33,   0x0B, 0x84, 0xE8, 0x5B, 0x9E, 0x82, 0x95, 0x49, 0x1B, 0x76,   0x0B, 0x87, 0x56, 0x36, 0xBB, 0x2E, 0xDC, 0xE0, 0x13, 0xF1,   0xE1, 0x91, 0x11, 0x40, 0x46, 0xA3, 0x8E, 0x6B, 0x0B, 0xC2,   0x19, 0xE0, 0x2B, 0x32, 0x7C, 0x81, 0x22, 0x12, 0xE9, 0xE0,   0x58, 0x05, 0x08, 0x56, 0x46, 0x83, 0xD8, 0xB9, 0x9B, 0x3F,   0xBE, 0xC6, 0x3B, 0x43, 0x6F, 0x57, 0x17, 0x8E, 0xDE, 0x21,   0x25, 0x9E, 0x2C, 0xD3, 0x10, 0xC2, 0x9B, 0x47, 0xAF, 0xB4,   0xD3, 0xDD, 0x05, 0xD8, 0x0C, 0xF2, 0x69, 0x9A, 0x33, 0xB1,   0xFD, 0x1E, 0xEB, 0x3F, 0x4C, 0x5B, 0xCD, 0x22, 0x38, 0xB5,   0x80, 0xC0, 0x88, 0xDD, 0x9A, 0xB5, 0xF6, 0xB5, 0x63, 0x13,   0x45, 0x70, 0xF4, 0xD8, 0x39, 0x59, 0x5E, 0xBE, 0x02, 0x0D,   0xB6, 0xC7, 0x43, 0x43, 0x4F, 0x49, 0xF1, 0xA6, 0x3C, 0xDD,   0x5F, 0xC1, 0xF9, 0x35, 0x2D, 0xA1, 0x97, 0xC7, 0x3F, 0xB6,   0xCD, 0x2F, 0x62, 0x45, 0x1F, 0xE0, 0x6D, 0x65, 0x5E, 0xFE,   0x8B, 0xF9, 0xB8, 0xE1, 0xCE, 0xF7, 0xCB, 0xDE, 0xD2, 0x55,   0x72, 0xA8, 0x26, 0xF2, 0x11, 0x2F, 0x75, 0xFA, 0x8C, 0x23,   0x60, 0xFD, 0x6F, 0x0E, 0xFD, 0xB3, 0xAD, 0x88, 0x47, 0xB7,   0x6C, 0x49, 0xE7, 0x6B, 0x76, 0x4F, 0xFB, 0xF2, 0x5B, 0x94,   0x0F, 0xB4, 0x65, 0x70, 0x84, 0x99, 0xA2, 0x0E, 0x8F, 0xBE,   0x38, 0x09, 0x01, 0x9B, 0x9D, 0x1C, 0xD7, 0xBD, 0xCB, 0x74,   0x5F, 0xFB, 0x11, 0x9B, 0xF4, 0x62, 0x8A, 0xD6, 0xBF, 0xEF,   0x94, 0x72, 0x86, 0x27, 0xCD, 0x2E, 0x36, 0x03, 0xFB, 0xDD,   0x32, 0xF4, 0x56, 0xC5, 0xD5, 0x4A, 0x68, 0x48, 0xC5, 0x28,   0x72, 0x61, 0x18, 0x10, 0xF3, 0x00, 0xA8, 0x1C, 0x45, 0xEF,   0x6D, 0x07, 0xAB, 0xDE, 0x80, 0x4A, 0xCA, 0xA3, 0xFC, 0x5A,   0x92, 0xE0, 0x78, 0x88, 0xC6, 0x4E, 0x36, 0xEE, 0x4E, 0x28,   0x05, 0xB2, 0xF7, 0xF2, 0xAC, 0xB8, 0x58, 0xF3, 0x99, 0x9D,   0x23, 0x8D, 0x41, 0x65, 0x9F, 0xEB, 0x76, 0xC0, 0x2E, 0xC6,   0x66, 0x52, 0x0E, 0x06, 0x6A, 0x38, 0x63, 0xDA, 0x2F, 0x71,   0x1B, 0xE7, 0x73, 0x96, 0x8B, 0x91, 0x33, 0x4B, 0x7C, 0x46,   0xA0, 0x9D, 0x9D, 0x3C, 0xA0, 0x20, 0x66, 0x03, 0x2B, 0x1C,   0x14, 0xED, 0x53, 0x67, 0x20, 0xF7, 0xFE, 0xB5, 0xA0, 0x3B,   0x59, 0xEE, 0x90, 0x02, 0xFB, 0x9A, 0x05, 0x47, 0xDC, 0xC6,   0x98, 0xEA, 0xCA, 0xD7, 0x09, 0x69, 0x70, 0x59, 0xB4, 0x68,   0x3C, 0xC2, 0xB6, 0x5F, 0x63, 0xEA, 0x62, 0x6F, 0x6B, 0xAC,   0x22, 0xAD, 0xB8, 0x2B, 0x36, 0x3B, 0x2B, 0xB7, 0xB8, 0x75,   0xCB, 0xCD, 0xD5, 0x3B, 0x79, 0xC7, 0x19, 0x4B, 0xF1, 0xA9,   0xB1, 0xD5, 0xC4, 0x59, 0x57, 0xAD, 0x5A, 0xA8, 0x28, 0x8E,   0xD7, 0x1E, 0x92, 0x6C, 0x01, 0x85, 0x13, 0x51, 0x62, 0x81,   0x65, 0xEA, 0x84, 0x57, 0x6F, 0x97, 0xB6, 0x0A, 0x37, 0xE0,   0x1D, 0x1E, 0x80, 0x04, 0x34, 0xC7, 0x7D, 0xBA, 0x74, 0x40,   0xD4, 0x6A, 0x72, 0xC2, 0xA1, 0x96, 0x3A, 0xF8, 0x5A, 0x9D,   0xA0, 0x50, 0xC3, 0x27, 0xF9, 0x96, 0x7F, 0x88, 0x41, 0x13,   0xE7, 0xAB, 0xAC, 0x7E, 0x77, 0xE2, 0x94, 0x67, 0x41, 0x11,   0x0D, 0xFB, 0xF2, 0x73, 0xDA, 0x18, 0x2F, 0x1C, 0xD5, 0x6B,   0xEC, 0xDE, 0x96, 0x4B, 0x83, 0x1A, 0xD6, 0xF3, 0x10, 0x9A,   0x4B, 0x8E, 0xBB, 0x2E, 0x74, 0x6D, 0x97, 0x0A, 0xCE, 0xC8,   0xC4, 0xFA, 0x4A, 0xAC, 0xB4, 0x6E, 0xDE, 0xAC, 0x58, 0xD2,   0xE1, 0x62, 0x38, 0x99, 0xAB, 0x92, 0xAE, 0xBD, 0x84, 0x52,   0x7D, 0x38, 0xFE, 0xAA, 0x6E, 0x14, 0x04, 0xA3, 0xB1, 0x72,   0xCB, 0x55, 0x97, 0x91, 0xF8, 0x31, 0x7E, 0xA9, 0x75, 0x13,   0xC0, 0xF9, 0xE2, 0x22, 0x63, 0x8F, 0xD2, 0x68, 0x3A, 0x97,   0xD7, 0x9E, 0x5B, 0xB9, 0xDE, 0xB8, 0x94, 0xA8, 0xAA, 0x34,   0x25, 0xF2, 0xC6, 0xC6, 0x81, 0xEE, 0xC8, 0x39, 0x40, 0x2B,   0x74, 0xE5, 0x52, 0x2A, 0xB9, 0x21, 0x92, 0xE8, 0x64, 0x4E,   0x24, 0x90, 0xDA, 0xD7, 0xDB, 0x67, 0x63, 0xA4, 0x8E, 0x03,   0x95, 0xD7, 0x2C, 0x87, 0x95, 0x50, 0x97, 0x8E, 0x27, 0xCC,   0x3B, 0xC7, 0x6B, 0x8E, 0x96, 0x69, 0x49, 0x07, 0x1C, 0xD1,   0x6A, 0x8E, 0x2A, 0x61, 0x26, 0xA0]
    a1 = 4284256177
    a2 = 1234567890
    a3 = 1095061718431
    v5 = 4
    flag = n2s(0x8B9551FA * a3)[::-1][:v5]
    #  print flag

    while a2:
        v3 = v5
        v5 += 1
        c = chr(((junk_data[a1 & 0xFFF] ^ a2) & 0xFF) % 128)
        #  flag += c if 0x20 <= ord(c) < 0x7f else ""
        flag += c
        #  print ord(c)
        a1 *= 77777
        a2 = a3 ^ (a2 >> 1)
        a3 >>= 1


    flag += c
    print flag
    print len(flag)
    print v5

if __name__ == "__main__":
    getInput()
    #  [a2 = 1234567890, a1 = 2136772529]
    #  [a2 = 1234567890, a1 = 4284256177]
    key3 = getKey3()
    print "key3 -> %d" % key3
    getFlag()

但奇怪的是用这个脚本没有跑出后几位flag,可能是python和C在边界处理上有区别

于是我换了一种方法,源程序中运算较慢是因为calc_key3这个函数耗费了大量的时间,把这个函数patch掉

然后在调试过程中手动给v7赋给我们计算出来的返回值,这样就可以得到flag了

0x0A unpackme

这道题目挺有意思, 需要对PE程序的结构有一些了解, 从题干已经可以知道这是一个加壳的程序,下载下来后 upx -d 脱壳失败,看来应该是壳的某些关键信息被改掉了,我们拿一个同样的 PE32(GUI) 程序加壳,比较一下文件结构,先用 strings/rabin2 -zzqq 比较两个binary的字符串,可以发现 unpackme.exe 中有很明显的人工修改过的字符串,如 CTF0, CTF1, CTF2, CTF?, !The flag is not FLAG{Hello,DOS section} 等. 放到 010editor 中使用 exe templete 分析,通过对比可以发现如假 FLAG 所说, section head被修改了,将 unpackme.exe 中的信息修改回来:

1
2
3
4
5
CTF0 -> UPX0
CTF1 -> UPX1
CTF2 -> .rsrc
CTF? -> UPX!
!The flag is not FLAG{Hello,DOS section} -> !This program cannot be run in DOS mode.

修改之后,就可以 upx -d 脱壳了,放入IDA中分析,可以看出关键代码如下:

 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
    v4 = GetParent(hWnd);
    v5 = GetDlgItemTextA(v4, 101, &String, 63);
    if ( v5 )
    {
      phProv = 0;
      phHash = 0;
      if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000)
        || !CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash)
        || !CryptHashData(phHash, (const BYTE *)&String, v5, 0)
        || !CryptGetHashParam(phHash, 2u, (BYTE *)pbData, &pdwDataLen, 0) )
      {
        ExitProcess(1u);
      }
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      v7 = pbData;
      v8 = &unk_4128A0;
      v9 = 12;
      while ( *(_DWORD *)v7 == *v8 )
      {
        v7 += 4;
        ++v8;
        v10 = v9 < 4;
        v9 -= 4;
        if ( v10 )
        {
          v11 = String;
          v12 = 0;
          v13 = 3;
          v14 = 2;
          do
          {
            Text[v12] = v11 ^ data[v12] ^ pbData[v12 & 0xF];
            Text[v12 + 1] = v11 ^ data[v12 + 1] ^ pbData[((_BYTE)v14 - 1) & 0xF];
            v15 = data[v14] ^ pbData[v14 & 0xF];
            v14 += 4;
            Text[v13 - 1] = v11 ^ v15;
            v16 = data[v13] ^ pbData[v13 & 0xF];
            v13 += 4;
            Text[v12 + 3] = v11 ^ v16;
            v12 += 4;
          }
          while ( v14 < 0x22 );
          if ( v12 < 0x80 )
          {
            Text[v12] = 0;
            MessageBoxA(hWnd, Text, aFlagIs, 0x40u);
            ExitProcess(0);
          }
          __report_rangecheckfailure();
          __debugbreak();
          JUMPOUT(*(_DWORD *)algn_40BDCF);
        }
      }
      MessageBoxA(hWnd, aWrongAnswer, aHackmectf_2, 0x10u);
    }

可以看出,只需满足 v8 = &unk_4128A0; 一句即可,后边的逻辑完全不用管,经过查API的文档,可以知道只需 MD5(input) == &unk_4128A0 即可, 而这个 hash 又能查出来

1
2
3
4
5
该数据已破解成功!
密文: 34AF0D074B17F44D1BB939765B02776F
明文: how[space]do[space]you[space]turn[space]this[space]on
数据来源: admin
用时:00:00:00.1911904

根据程序逻辑,在输入框中输入 how do you turn this on 就会弹出 flag, 但输入 flag 后又发现 check Password 按钮是灰色的不能点, 这个好办, 根据查阅 API 发现 EnableWIndow 函数做了限制, 当 EnableWindow 函数的第二个参数是 0 时, 控件无响应, 将次参数 patch 为 1 即可, 这时输入 how do you turn this on 就能弹 flag 了

多说一句, 这道题用 wine 模拟可以减少很多不必要的麻烦, 具体可以看这道题的 writeup

0x0B mov

这个题前前后后做了好几个月,后来发现是我想麻烦了,先说一下我做这道题的历程

  1. IDA打开之后看到一堆mov,根据提示*MOV instruction is turing complete!*很容易google到这是movfuscator混淆,同时找到了去混淆的工具demovfuscator,安装后(不是太好装)发现去混淆的效果并不理想,生成的CG也似乎有问题

  2. 又google到类似题目的writeup,有使用qira,有使用perf,有使用pin,有直接从内存中抠出来的,因为最近在看fuzz的一些东西,所以想尝试使用pin解题目

  3. 大致了解了pin的工作原理后,安装好环境,开始找输入对输出的影响,这时偶然发现一个bug,输入n位字符时,这n位与flag的前n位相等即可,可利用这个特性爆破

    爆破脚本

     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
    
    inndy_mov [master] cat solve.py 
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    __Auther__ = 'M4x'
    
    from subprocess import Popen, PIPE
    from string import printable
    
    ans = "FLAG{"
    while True:
        if "}" in ans:
            print ans
            break
    
            
        for c in printable:
            f = Popen("./mov", shell = True, stdin = PIPE, stdout = PIPE)
            tmp = ans + c
            f.stdin.write(tmp + "\n")
                
            stdout, stderr = f.communicate()
            if "Good" in stdout:
                ans = tmp
                print ans
                break
    

    实际效果也很好,不到2s就爆破出了flag

觉得使用pin的方法很巧妙,以后再补上使用pin的wp

Crypto

0x00 xor

zwhubuntu师傅告诉我这题可以用xortool解,项目地址https://github.com/hellman/xortool

主要记录一下使用方法

0x01 ffa

这个题就只想说z3大法好了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python3
import sympy
import json

m = sympy.randprime(2**257, 2**258)
M = sympy.randprime(2**257, 2**258)
a, b, c = [(sympy.randprime(2**256, 2**257) % m) for _ in range(3)]

x = (a + b * 3) % m
y = (b - c * 5) % m
z = (a + c * 8) % m

flag = int(open('flag', 'rb').read().strip().hex(), 16)
p = pow(flag, a, M)
q = pow(flag, b, M)

json.dump({ key: globals()[key] for key in "Mmxyzpq" }, open('crypted', 'w'))
# {"p": 240670121804208978394996710730839069728700956824706945984819015371493837551238, "q": 63385828825643452682833619835670889340533854879683013984056508942989973395315, "M": 349579051431173103963525574908108980776346966102045838681986112083541754544269, "z": 213932962252915797768584248464896200082707350140827098890648372492180142394587, "m": 282832747915637398142431587525135167098126503327259369230840635687863475396299, "x": 254732859357467931957861825273244795556693016657393159194417526480484204095858, "y": 261877836792399836452074575192123520294695871579540257591169122727176542734080}

程序的逻辑很简单,生成了几个大随机数M, m, x, y, z, p, q,通过分析代码不难得出如果得到a或者b就能得到flag,但问题就在于,怎么求出a或者b。

刚开始的想法是通过有限域的方法化简,得到a或者b,但正要动手时转念一想,题上的约束关系都很明确,可以用z3试一下,于是写了z3求解的代码:

 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
def getabc():
    a = BitVec("a", 265)
    b = BitVec("b", 265)
    c = BitVec("c", 265)
    x = d["x"]
    y = d["y"]
    z = d["z"]
    m = d["m"]
    #  print x, y, z
    s = Solver()
    s.add(UGT(a, pow(2, 256, m)))
    s.add(ULT(a, pow(2, 257, m)))
    s.add(UGT(b, pow(2, 256, m)))
    s.add(ULT(b, pow(2, 257, m)))
    s.add(UGT(c, pow(2, 256, m)))
    s.add(ULT(c, pow(2, 257, m)))
    s.add(x == (a + b * 3) % m)
    s.add(y == (b - c * 5) % m)
    s.add(z == (a + c * 8) % m)
    
    while s.check() == sat:
        if isPrime(s.model()[a].as_long()) and isPrime(s.model()[b].as_long()) and isPrime(s.model()[c].as_long()):
            print s.model()
        A, B, C = s.model()[a].as_long(), s.model()[b].as_long(), s.model()[c].as_long()
        s.add(Or(a != s.model()[a], b != s.model()[b], c != s.model()[c]))
    else:
        print "Finished"
        return A, B, C

有两点需要注意:

  1. 用BitVec声明变量时,首先要估算中间变量的范围,确保运算过程中数据不会溢出(如该题中使用了较大的265位),但为了运行速度也不能过大
  2. 大整数的比较大小建议使用UGT/ULT代替>/<

本来运行的时候还替z3担心了一下会不会因为数据太大直接崩掉,但没想到用了短短的8s就跑出了结果

计算出a,b,c后,主要问题就解决了。

意识到使用flag的加密实际上是RSA时,利用a和b进行了共模攻击,很快就出flag了

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'

from libnum import prime_test as isPrime
from libnum import n2s, xgcd, invmod
from z3 import *

d = {"p": 240670121804208978394996710730839069728700956824706945984819015371493837551238, "q": 63385828825643452682833619835670889340533854879683013984056508942989973395315, "M": 349579051431173103963525574908108980776346966102045838681986112083541754544269, "z": 213932962252915797768584248464896200082707350140827098890648372492180142394587, "m": 282832747915637398142431587525135167098126503327259369230840635687863475396299, "x": 254732859357467931957861825273244795556693016657393159194417526480484204095858, "y": 261877836792399836452074575192123520294695871579540257591169122727176542734080}

def getabc():
    a = BitVec("a", 265)
    b = BitVec("b", 265)
    c = BitVec("c", 265)
    x = d["x"]
    y = d["y"]
    z = d["z"]
    m = d["m"]
    #  print x, y, z
    s = Solver()
    s.add(UGT(a, pow(2, 256, m)))
    s.add(ULT(a, pow(2, 257, m)))
    s.add(UGT(b, pow(2, 256, m)))
    s.add(ULT(b, pow(2, 257, m)))
    s.add(UGT(c, pow(2, 256, m)))
    s.add(ULT(c, pow(2, 257, m)))
    s.add(x == (a + b * 3) % m)
    s.add(y == (b - c * 5) % m)
    s.add(z == (a + c * 8) % m)
    
    while s.check() == sat:
        if isPrime(s.model()[a].as_long()) and isPrime(s.model()[b].as_long()) and isPrime(s.model()[c].as_long()):
            print s.model()
        A, B, C = s.model()[a].as_long(), s.model()[b].as_long(), s.model()[c].as_long()
        s.add(Or(a != s.model()[a], b != s.model()[b], c != s.model()[c]))
    else:
        print "Finished"
        return A, B, C

def getFlag((a, b, c)):
    M = d["M"]
    p = d["p"]
    q = d["q"]
    s1, s2, _ = xgcd(a, b)
    if s1 < 0:
        s1 = -s1
        p = invmod(p, M)
    elif s2 < 0:
        s2 = -s2
        q = invmod(q, M)

    flag = (pow(p, s1, M) * pow(q, s2, M)) % M
    print n2s(flag)
    

if __name__ == "__main__":
    #  getabc()
    getFlag(getabc())

整个脚本也才8s左右

OwO多了一步求flag的过程,计算时间反而更短了,看来计算时间还是跟CPU的心情有关系

Programming

fast

ppc类型的题目,注意数据范围在int32范围内即可,同时注意如果使用pwntools写socket的话,没有必要recvuntil后再send,完全可以现将所有的输入输出都存到缓冲区中,最后一次性send,具体看脚本

 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
inndy_fast [master] cat exp.py 
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'

from pwn import *
from numpy import int32
import time
import re
#  context.log_level = 'debug'

#  io = process("./fast")
io = remote("hackme.inndy.tw", 7707)

io.sendlineafter("the game.\n", "Yes I know")

ans = ""
res = ""
f = lambda x: int32(int(x))
for i in xrange(10000):
    n1, op, n2 = io.recvuntil("=", drop = True).strip().split(' ')
    #  print n1, op, n2
    io.recvline()

    if op == '+':
        #  print n1, op, n2
        ans = str(f(n1) + f(n2))
    if op == '-':
        ans = str(f(n1) - f(n2))
    if op == '*':
        ans = str(f(n1) * f(n2))
    if op == '/':
        ans = str(int(float(n1) / int(n2)))

    res += (ans + " ")

#  print res
io.sendline(res)
io.interactive()
io.close()