n1cef1sh's Blog

前言

这算是参加的第一次正式的线下赛吧,根据之前两次小组赛的情况预估,感觉自己拿前三都有些不稳。没想到这次的杂项和密码拯救了我,有些走运地成了第一。p0desta表哥最后的取证hint是最到位的了(跪),先记录一下表哥表姐辛苦出的题目,最后再写总结。

题解过程

MISC

签到题

向公众号发个give me flag即可

找到了也打不开

下载一个压缩包解压后得到一个名为helloboy.pcapng的文件,虽然后缀被篡改了,但是文件类型还是显示的Wireshark capture file,于是把后缀改成pcap,用wireshark打开。有非常多的数据,习惯性的先筛了一下http,发现只有一条,查看这个数据包,数据内容是一个flag.zip,所以直接导出这个压缩包。但是解压需要密码,寻找后未找到相关提示,尝试修改伪加密,成功得到flag。

真香

铁骨铮铮王境泽,一碗炒饭可叛国。看到亲切的王境泽gif,脑海里回想起经典的好汉歌鬼畜“我王境泽就算饿死,死在外边从这跳下,不会吃一点东西啊,诶嘿诶嘿诶嘿诶嘿,真香!真香!”
直接用binwalk分析,发现隐藏了一个zip,用foremost -T命令分解,但是解压需要密码,查看属性里的注释发现了“blind_water _mark”,尝试一下果然是密码,同时也知道了这是一个盲水印的题目。解压后得到两个一样的png,用脚本直接解盲水印即可。

Just Listen

也是一道常规题目,mp3隐写。我印象很深是因为原来平台上放过这种题目,而有两个同学在群里问过mp3steg那个工具使用命令的问题。他们的名字都有点像小姐姐,然而其实都是猛男……看了一下这次他俩都没有做出这个题目,有点可惜。
给了一个mp3和一个叫password的png,不过png是空白的,用stegsolve偏移一下即可得到key。然后直接用mp3steg工具。

Decode -X 晴天.mp3 -P forensics_is_fun

取证

这是我最后做出的一个题目,前期的时候想做但是没有思路,也就没有仔细考虑下去。最后时间不多了,决定再尝试一波,因为之前做过几个内存取证的题目,有一点点笔记和底气。
题目是一个vmdx文件,虚拟机磁盘文件。直接用VM打不开,用volatility分析也没有结果。后来发现VM里有个映射虚拟磁盘的选项,遂尝试映射该vmdx文件,成功的在映射出的磁盘空间里找到一个nothing.rar,把它拷贝出来解压。有一个nothing.gif文件,又是一个gif,我首先还是用binwalk分析,没有隐藏东西。然后又考虑隐藏到每一帧的做法,identify也没有分析出数据。一筹莫展之际,p0desta表哥及时的放出了hint“NTFS”。这终于是我会做的简单的数据流隐写了,赶忙找出NTFS Streams Editor,扫描一下nothing.gif所在的目录,成功找到了flag.gif。因为每一帧比较快,直接找网站将每一帧图片分离,GET到flag。 ==必须用WINRAR解压,才能扫描出隐藏数据流==

i_wanna_get_the_flag

解压后是个exe小游戏和一个游戏说明?第一次碰到这种题目,感觉很有意思。shift是跳跃,z是发射子弹,就类似一个超级玛丽的闯关游戏,但是有十二关,而且不太好操作(手残路过,勉强到第三关就进行不下去了。进入游戏后目录下生成了controls和DeathTime两个文件,而载入游戏或是存档后会生成另外两个文件save1(依次递增 表示存档点)和temp。放出hint以后提示关注这几个文件,但是也并没有意识到修改存档文件的操作……说起来当年玩单机游戏用金山游侠V修改存档和本地数据也是很熟练……这就是第一种做法,通过闯关对比出save文件中表示关卡数的标志位,修改成大于12(因为一共有12关)的数再加载游戏即可得到flag。
第二种做法也很easy,打通关就可以了嘛……QAQ
第三种做法说来惭愧,这是一个exe,竟然没有去re它的意识……用IDA没有翻出来……据说这是用GameMarker软件用delphi语言做的游戏,可是没找到GameMarker Decompiler的资源TAT……果然还是走常规的路吧

Crypto

没错,是这里面的题把我心态搞崩了……

看到的就是全部

题目是一串unicode编码,解码后得到俩花括号和字母数字的字符串,但是好像没有SKCTF这样类似的字眼,于是尝试栅栏,但是我的工具栅栏失败了……又懒得手算,于是凯撒了一下,找到一组包含SKCTF等字眼的,拿出来以后陷入沉思。有没有方法可以让这个字符串排列组合呢……突然想起题目描述里有个Welcome!瞬间开窍开始自己组合这个字符串……于是尴尬的事情就发生了。 原本的flag是SKCTF{We1cOmE2sKCTF3rd} 但是我的神奇脑回路下,拼成了SKCTF{We1cOmE2sKdCTF3r} 也就是 “欢迎来山科大啊各位兄弟们”……然后就是把e E 3疯狂交换位置,然后把CTF3r放到最前面尝试……我觉得这语句也很通顺啊于是一直这么试……后来想通的时候觉得自己傻的不行。

仿射变换

常规的仿射密码,直接根据它给的关系式写脚本解即可

timu = 'fwpcpywpcphnxaoxlywpcphnxlhco'

a = []
for i in timu:
    b = ord(i) - 97
    a.append(b)

for i in a:
    for j in range(0,26):
        c = (11*j + 23)%26
        if(c == i):
          print chr(j+97),

Play fair

没有错,是这个题给了我会心一击……我在这个题上耗了一个多小时和所有耐心……首先这个密码类型我是知道的,迅速查了加密规则,手动做了一波,解出来是杂乱的字符串,凯撒栅栏都没啥结果。后来给hint说加密后的字符串就是flag,但是怎么试都不对呢。于是我找了个网上的程序自动跑出结果,仍然不对。这时候我身后已经有几个表哥表姐站在身后看不下去了……
“你再仔细看看规则”
“先做做别的要不”
本来很有把握迅速做出来的题目,却迟迟找不到错误的地方,一遍遍的重新手算,后来甚至听到身后的表姐说“咦我感觉好像没错呀……”
最后改出答案后我想明白了。关于里面的两个字母在同一列的时候,选择下方的字母,如果这个字母在最后一行的话,是选同一列的最上面一行的字母,还是选择右边一列的最上面一行的字母,这个规则是有区别的……而且我把加密解密的方向搞混了,就一直有两组字母是反着的……
这道题卡的我耐心全无,什么也做不下去……心态还是要好好锻炼呀……

easy rsa

这是另一道很可惜的题目。p0desta都看不下去了过来给我指了指这个题,但我还是没能领会其中要义……
题目给了hint“低指数攻击”,我迅速想到了之前看过的姿势分享文章里提到过。顺利找到了那个文章,模仿着脚本爆破。但致命的问题就是,我忽视了最根本的东西……忘记了我爆破的这个m就是代表的RSA中的密文……

C=m^e mod n

虽然其中的数学问题不太懂,但是已知了c n,而且提示了低指数,一般情况e都是3(况且后来给了第二个hint就是e=3)。所以直接按照脚本爆破出m就好了……
幸运的是我爆破出来了,但是我不知道这串东西有啥用……甚至没有看到文章最后写的那个步骤“把m转换成字符串的形式”……十分僵硬啊
而且我看我跑脚本的结果少了一些k的输出,于是照猫画虎的把c的值从十进制转成了十六进制继续跑……这一跑不得了,虽然和预想一样开始输出k的值了,但是跑了两千多万还没有结果,电脑还因此卡的一批。我数了数文章里那个例题,跑了一亿多次。我心想,完了,这个题跑不完了……然而然而……
结果虽然可惜,但是学到了一个RSA的新姿势,也能牢牢记住了……mark下这个脚本

from gmpy2 import iroot
c=20007698782339834246219328724588364459038474898597431254441716723329047692482286669616878491497181438826429104573158490197780427343059002311390665150204203593904674308567972583524863664412573864470357485299378319457792659062998310715680020892095430469672971488726545126438904961637
n=127736277372703302601056543119422673263688414162130452012271136376613506149677023810059879551077756689012265602068781726322860263396788055341495459092641851239465778777954763378423777055786390661741851297248439205933852145044703803764388021585419049988085302710871206091591995497858682344782684500134395300049
k=0

while 1:
	res = iroot(c+k*n, 3)
	if(res[1] == True):
		print res
		break
	print "k=" + str(k)
	k = k+1

from libnum import s2n,n2s
m = 2714765867087142227682974855518723609751025031426965627280810289532453865429373729956716427133
print n2s(m)

需要装一个libnum的模块,目前只能用于python2。这个模块包含了很多数学运算,结合gmpy2和PyCrypto可以很方便的进行计算和爆破。列举几个libnum的应用

数字型(不论是十六进制还是十进制)与字符串之间的转换:

import libnum
s="flag{nicefish}"
print libnum.s2n(s)
import libnum
n=0x666c61677b706361747d
print libnum.n2s(n)

这个转换不用在意十六进制的位数是否为偶数

生成质数:

print libnum.generate_prime(1024)

因数分解:

print libnum.factorize(1024)

流密码

这个题目也放了很多hint,但是因为时间和心态问题没有去仔细看。对流密码还是有点不太理解,先附一下表姐的题解学习。

根据图可知,Y是LFSR产生序列与FLAG异或得到的结果。已知了Y,求出加密时使用的序列即可。       
LFSR初始状态是17,即10001 将其表示为a4a3a2a1a0
首先输出的是a0 也就是LFSR的右端是a0,顺序向左排列。第一次运算,移位输出为a0,将a0和a3异或得到a5,a5=a0^a3。第一次运算后,LFSR从右向左依次是11000.重复操作,继续输出。推广为:      
n>5时,a(n) = a(n-5) ^ a(n-2)

又知道Y的长度是184位,依次运算生成184位初始序列然后和Y进行异或即可得到FLAG的二进制。转成十六进制再转成字符串即可。

# - UTF-8 
# LSFR_decode
# _Bonjour
#
Y
=
"11011100110100010000000111101111010110010100111111000001010001100110000100
110000011001011011100100100011100110010111101111101000100011101111101101100
11111010111110001100001101000101010"
len = len(Y)
print(len) # 求密文长度
# initial_state = 17
# initial_state = "00010001"
output = '10001'
# 根据图(a4,a3,a2,a1,a0)҅ f = list[i] ^ list[i + 3]
for i in range(184):
output += str(int(output[i]) ^ int(output[i + 3]))
print("output = " + output)
flag = ''
for i in range(184):
flag += str(int(output[i]) ^ int(Y[i]))
print("flag = " + flag)
list = ''
idx = 0
while idx < len:
list += chr(int(flag[idx:idx+8],2))
idx += 8
print(list)

RE & Android

各做了一个签到题……过程还很纠结,以后应该考虑先做这部分题,不然被杂项密码搞崩心态就很静下心来分析了……

getIt

一言难尽的题目……(是的 也忘记了查壳的好习惯)IDA打开后直接查看了字符串,虽然很多但是还是找到了关键的部分。 image 那么U7kD0VwFt2d0DUUfVxdQEMl=ZVd=这一个字符串就是关键了,但是看不出来是经过了什么样的加密过程……也确实脑子里乱乱的,纠结了好一会不知道怎么下手。这里必须要感谢Y表姐的画外音了……emmm这可能是她出的题吧,两次从我身边走过发现我看这个题发呆,好心的提醒我别想复杂题目不难……我仍旧没有会意……最后表姐还是耐心的提醒我,看字符串没有想到什么吗……
呀我打开CTFcrack一通狂轰乱炸,终于在栅栏里发现了猫腻……

U0tDVEZ7V2UxMVkwdUdldDF0fQ==

好的吧,密码学果然是无处不在的……只是加了个RE的外衣吗……真的很感谢表姐的耐心提醒,做出这个题以后心态好多了,才能最后又有心思再做一个杂项题目。QAQ

爆破大法好

这个就不多赘述,JEB里一看源码,是MD5,不可逆,也就是所谓的爆破了。直接查询MD5即可。

CreakMe

刚刚才发现好像题目错了?creak是“发出咯吱声”的意意思噗……
说起来这个题目也不复杂,虽然是mac程序,但是强大的IDA也能分析。就三层逻辑,但是当时没有搞懂这个vars0和输入的v1是个什么关系……其实应该下手试一试的,逆着逻辑写出脚本就可以直接得到flag了。还是太菜了。 image image image

def calc(result):
    for i in range(32,128):
        k = i
        for j in range(4):
            tmp = k*2
            if tmp & 0x100:
                tmp |= 1
            k = tmp & 255
        if k == result:
            return chr(i)
string='44F6F557F5C696B656F51425D4F596E63747275736479603E6F3A392'.decode('hex')
flag = ''
for ch in string:
flag+=calc(ord(ch))
print flag

babyre

这个题目和之前小组赛的一个题目基本是一样的,就是稍微改了改数据。时间够的话也能做……唉。

IDA64打开后提示DWARF plugin encountered a recoverable error。无法F5,就改用gdb调试(直接IDA里看也一样 但是不如gdb里看着舒服点)。
image 在主函数下断 然后反汇编,读汇编代码

gdb-peda$ b main.main
Breakpoint 1 at 0x48ea30: file /root/Desktop/babyre.go, line 7.
gdb-peda$ disas main.main
Dump of assembler code for function main.main:
   0x000000000048ea30 <+0>:	mov    rcx,QWORD PTR fs:0xfffffffffffffff8
   0x000000000048ea39 <+9>:	lea    rax,[rsp-0x8]
   0x000000000048ea3e <+14>:	cmp    rax,QWORD PTR [rcx+0x10]
   0x000000000048ea42 <+18>:	jbe    0x48ebda <main.main+426>
   0x000000000048ea48 <+24>:	sub    rsp,0x88
   0x000000000048ea4f <+31>:	mov    QWORD PTR [rsp+0x80],rbp
   0x000000000048ea57 <+39>:	lea    rbp,[rsp+0x80]
   0x000000000048ea5f <+47>:	lea    rax,[rip+0x1087a]        # 0x49f2e0
   0x000000000048ea66 <+54>:	mov    QWORD PTR [rsp],rax
   0x000000000048ea6a <+58>:	call   0x40e470 <runtime.newobject>
   0x000000000048ea6f <+63>:	mov    rax,QWORD PTR [rsp+0x8]
   0x000000000048ea74 <+68>:	mov    QWORD PTR [rsp+0x48],rax
   0x000000000048ea79 <+73>:	mov    ecx,0x1
   0x000000000048ea7e <+78>:	jmp    0x48eb09 <main.main+217>
   0x000000000048ea83 <+83>:	inc    rcx
   0x000000000048ea86 <+86>:	cmp    rcx,0xf
   0x000000000048ea8a <+90>:	jge    0x48ebb9 <main.main+393>
   0x000000000048ea90 <+96>:	mov    rdx,QWORD PTR [rax+0x8]
   0x000000000048ea94 <+100>:	mov    rbx,QWORD PTR [rax]
   0x000000000048ea97 <+103>:	cmp    rcx,rdx
   0x000000000048ea9a <+106>:	jae    0x48ebd3 <main.main+419>
   0x000000000048eaa0 <+112>:	movzx  edx,BYTE PTR [rbx+rcx*1]
   0x000000000048eaa4 <+116>:	lea    rbx,[rip+0x324b8]        # 0x4c0f63
   0x000000000048eaab <+123>:	movzx  esi,BYTE PTR [rbx+rcx*1]
   0x000000000048eaaf <+127>:	xor    edx,esi
   0x000000000048eab1 <+129>:	cmp    dl,0x3
   0x000000000048eab4 <+132>:	jne    0x48eb6f <main.main+319>
   0x000000000048eaba <+138>:	cmp    rcx,0xe
   0x000000000048eabe <+142>:	jne    0x48ea83 <main.main+83>
   0x000000000048eac0 <+144>:	xorps  xmm0,xmm0
   0x000000000048eac3 <+147>:	movups XMMWORD PTR [rsp+0x50],xmm0
   0x000000000048eac8 <+152>:	lea    rax,[rip+0x10811]        # 0x49f2e0
   0x000000000048eacf <+159>:	mov    QWORD PTR [rsp+0x50],rax
   0x000000000048ead4 <+164>:	lea    rcx,[rip+0x42df5]        # 0x4d18d0 <main.statictmp_1>
   0x000000000048eadb <+171>:	mov    QWORD PTR [rsp+0x58],rcx
   0x000000000048eae0 <+176>:	lea    rdx,[rsp+0x50]
   0x000000000048eae5 <+181>:	mov    QWORD PTR [rsp],rdx
   0x000000000048eae9 <+185>:	mov    QWORD PTR [rsp+0x8],0x1
   0x000000000048eaf2 <+194>:	mov    QWORD PTR [rsp+0x10],0x1
   0x000000000048eafb <+203>:	call   0x4831a0 <fmt.Println>
   0x000000000048eb00 <+208>:	xor    eax,eax
   0x000000000048eb02 <+210>:	mov    ecx,eax
   0x000000000048eb04 <+212>:	mov    rax,QWORD PTR [rsp+0x48]
   0x000000000048eb09 <+217>:	test   cl,cl
   0x000000000048eb0b <+219>:	je     0x48ebc3 <main.main+403>
   0x000000000048eb11 <+225>:	mov    BYTE PTR [rsp+0x47],cl
   0x000000000048eb15 <+229>:	xorps  xmm0,xmm0
   0x000000000048eb18 <+232>:	movups XMMWORD PTR [rsp+0x70],xmm0
   0x000000000048eb1d <+237>:	lea    rcx,[rip+0xda1c]        # 0x49c540
   0x000000000048eb24 <+244>:	mov    QWORD PTR [rsp+0x70],rcx
   0x000000000048eb29 <+249>:	mov    QWORD PTR [rsp+0x78],rax
   0x000000000048eb2e <+254>:	lea    rdx,[rip+0x30dc8]        # 0x4bf8fd
   0x000000000048eb35 <+261>:	mov    QWORD PTR [rsp],rdx
   0x000000000048eb39 <+265>:	mov    QWORD PTR [rsp+0x8],0x2
   0x000000000048eb42 <+274>:	lea    rbx,[rsp+0x70]
   0x000000000048eb47 <+279>:	mov    QWORD PTR [rsp+0x10],rbx
   0x000000000048eb4c <+284>:	mov    QWORD PTR [rsp+0x18],0x1
   0x000000000048eb55 <+293>:	mov    QWORD PTR [rsp+0x20],0x1
   0x000000000048eb5e <+302>:	call   0x489180 <fmt.Scanf>
   0x000000000048eb63 <+307>:	mov    rax,QWORD PTR [rsp+0x48]
   0x000000000048eb68 <+312>:	xor    ecx,ecx
   0x000000000048eb6a <+314>:	jmp    0x48ea86 <main.main+86>
   0x000000000048eb6f <+319>:	xorps  xmm0,xmm0
   0x000000000048eb72 <+322>:	movups XMMWORD PTR [rsp+0x60],xmm0
   0x000000000048eb77 <+327>:	lea    rax,[rip+0x10762]        # 0x49f2e0
   0x000000000048eb7e <+334>:	mov    QWORD PTR [rsp+0x60],rax
   0x000000000048eb83 <+339>:	lea    rcx,[rip+0x42d36]        # 0x4d18c0 <main.statictmp_0>
   0x000000000048eb8a <+346>:	mov    QWORD PTR [rsp+0x68],rcx
   0x000000000048eb8f <+351>:	lea    rdx,[rsp+0x60]
   0x000000000048eb94 <+356>:	mov    QWORD PTR [rsp],rdx
   0x000000000048eb98 <+360>:	mov    QWORD PTR [rsp+0x8],0x1
   0x000000000048eba1 <+369>:	mov    QWORD PTR [rsp+0x10],0x1
   0x000000000048ebaa <+378>:	call   0x4831a0 <fmt.Println>
   0x000000000048ebaf <+383>:	movzx  eax,BYTE PTR [rsp+0x47]
   0x000000000048ebb4 <+388>:	jmp    0x48eb02 <main.main+210>
   0x000000000048ebb9 <+393>:	movzx  eax,BYTE PTR [rsp+0x47]
   0x000000000048ebbe <+398>:	jmp    0x48eb02 <main.main+210>
   0x000000000048ebc3 <+403>:	mov    rbp,QWORD PTR [rsp+0x80]
   0x000000000048ebcb <+411>:	add    rsp,0x88
   0x000000000048ebd2 <+418>:	ret    
   0x000000000048ebd3 <+419>:	call   0x424f70 <runtime.panicindex>
   0x000000000048ebd8 <+424>:	ud2    
   0x000000000048ebda <+426>:	call   0x44d270 <runtime.morestack_noctxt>
   0x000000000048ebdf <+431>:	jmp    0x48ea30 <main.main>
End of assembler dump.

73行一层while循环,然后跳到217行,若符合条件,向下执行到302行调用scanf函数,然后再跳回86行,进入一个i=0,i< 15的循环,在116加载一个字符串【0x4cf63】可以用x /s 0x4c0f63 查看

gdb-peda$ x /s 0x4c0f63
0x4c0f63:	"PH@WExabazd3qf~Psalter_Pahlavi]\n\tmorebuf={pc:acquirep: p->m=advertise errorforce gc (idle)key has expiredmalloc deadlockmissing mcache?ms: gomaxprocs=network is downno medium foundno such processnot a"...

然后进行逐位异或,结果是1则right,否则wrong。

最后附一下源码

package main
import (
    "fmt"
        )
func main() {
    var i = true
    str := ""
    str2 := "PH@WExabazd3qf~"
    for i {
        fmt.Scanf("%s", &str)
        for b:=0; b < 15; b++ {
            if str[b] ^ str2[b] != 3 {
                fmt.Println("Wrong")
                break
            } else {
                if b == 14 {
                fmt.Println("Right")
                i = !true
                break
                }
            }
        }
    }
}

GetFlag

image 打开后有个网址链接,点击跳到一个输入密码的界面 image 放到JEB里反汇编,里面有个encrypt类,看里面的逻辑 image

String v3 = encrypt.this.editText.getText().toString().trim();
                    if(v3.length() != v12) {
                        encrypt.this.isflag = false;
                    }
                    else {
                        char[] v4 = v3.toCharArray();
                        int v1;
                        for(v1 = 0; v1 < 4; ++v1) {
                            int v8 = v4[v1 * 4];
                            int v2;
                            for(v2 = 0; v2 < 3; ++v2) {
                                v4[v1 * 4 + v2] = v4[v1 * 4 + v2 + 1];
                            }

                            v4[v1 * 4 + 3] = ((char)v8);
                        }

获取输入的字符串,长度为12位,然后进行移位操作。12位分成三组,每一组的四个数向左移位一个。

 Signature[] v7 = encrypt.this.getBaseContext().getPackageManager().getPackageInfo(encrypt.this.getPackageName(), 64).signatures;
                        char[] v6 = encrypt.getSignatureString(v7[0]).toLowerCase().toCharArray();
                        if(encrypt.this.debuggable) {
                            Log.d("sign", encrypt.getSignatureString(v7[0]).toLowerCase());
                        }

这是把APK的签名信息按小写字母打印出来。至于查看apk的签名信息,可以直接查看log。或者用了一个JDK自带的工具keytool。image

   MessageDigest v0 = MessageDigest.getInstance("SHA1");

根据上边的签名方法是SHA1,所以取第二行的数据。

B0:26:B3:4B:00:BB:4C:D2:86:21:75:D1:57:B1:AD:5E:E8:9A:40:7C

                        int[] v5 = new int[16];
                        for(v1 = 0; v1 < v12; ++v1) {
                            v5[v1] = v4[v1] ^ v6[v1 * 2];
                        }

                        if(encrypt.this.debuggable) {
                            for(v1 = 0; v1 < v12; ++v1) {
                                Log.d("s", String.valueOf(v5[v1]));
                            }
                        }

                        for(v1 = 0; v1 < v12; ++v1) {
                            if(v5[v1] != encrypt.result[v1]) {
                                encrypt.this.isflag = false;
                            }

把第一步移位操作的结果和SHA1数字签名的偶数位置进行异或,得到V5和最初给的result进行比较,相同则正确。
根据逻辑写脚本

from __future__ import print_function 
result = [13, 92, 5, 87, 81, 22, 65, 22, 89, 70, 94, 8, 91, 67, 64, 90]
sig = 'B026B34B00BB4CD2862175D157B1AD5EE89A407C'
sign = sig.lower()
#print sign

flag =''
for i in range(0,16):
	#flag += (int(result[i])^ ord(sign[i*2]))
	flag += (chr(int(result[i])^ ord(sign[i*2])))
#print flag

flag = list(flag)
for i in range(0,4):
	t = flag[(3-i)*4+3]
	for j in range(0,3):
		flag[(3-i)*4+(2-j)+1] = flag[(3-i)*4+(2-j)]
	flag[(3-i)*4] = t 

#print flag
for i in range(16):
	print (flag[i],end="")

image

总结

总的来说是一个好的中转站吧,但是也应该清楚自己比不了17级的年轻人们……对我而言大学已经度过一多半了,要抓紧时间多学点东西。