xctf-streamgame1

下载附件后,得到两个文件,一个key文件,一个streamgame1.py文件

key文件如下:(十六进制形式)

55 38 F7 42 C1 0D B2 C7 ED E0 24 3A

streamgame1.py如下:

 1 from flag import flag
 2 assert flag.startswith("flag{")
 3 # 作用:判断字符串是否以指定字符或子字符串开头flag{
 4 assert flag.endswith("}")
 5 # 作用:判断字符串是否以指定字符或子字符串结尾},flag{},6个字节
 6 assert len(flag)==25
 7 # flag的长度为25字节,25-6=19个字节
 8 #3<<2可以这么算,bin(3)=0b11向左移动2位变成1100,0b1100=12(十进制)
 9 def lfsr(R,mask):
10     output = (R << 1) & 0xffffff    #将R向左移动1位,bin(0xffffff)='0b111111111111111111111111'=0xffffff的二进制补码
11     i=(R&mask)&0xffffff             #按位与运算符&:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
12     lastbit=013     while i!=0:
14         lastbit^=(i&1)    #按位异或运算符:当两对应的  二进位相异时,结果为1
15         i=i>>1
16     output^=lastbit
17     return (output,lastbit)
18 
19 
20 
21 R=int(flag[5:-1],2)
22 mask    = 0b1010011000100011100
23 
24 f=open("key","ab")   #以二进制追加模式打开
25 for i in range(12):
26     tmp=0
27     for j in range(8):
28         (R,out)=lfsr(R,mask)
29         tmp=(tmp << 1)^out   #按位异或运算符:当两对应的二进位相异时,结果为1
30     f.write(chr(tmp))  #chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。
31 f.close()

这里给出两种做法,第一种直接爆破得出,第二种破解程序的算法得出。

方法①:通过py文件,可以知道flag{}内的字符个数为19个,且为2进制,因此将其转换为十进制,一定不超过2**19,脚本如下:

 1 from Crypto.Util.number import *
 2 a="55 38 F7 42 C1 0D B2 C7 ED E0 24 3A"
 3 a=a.split()
 4 print(a)
 5 def lfsr(R,mask):
 6     output = (R << 1) & 0xffffff    #将R向左移动1位,bin(0xffffff)='0b111111111111111111111111'=0xffffff的二进制补码
 7     i=(R&mask)&0xffffff             #按位与运算符&:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
 8     lastbit=0#奇偶数
 9     while i!=0:
10         lastbit^=(i&1)    #按位异或运算符:当两对应的  二进位相异时,结果为1
11         i=i>>1
12     output^=lastbit#最低为就是R中最后一位之前的1的奇偶数
13     return (output,lastbit)
14 
15 mask    = 0b1010011000100011100
16 for R in range(2**19):
17     index=1
18     #print("R:",R)
19     temp=R
20     for i in range(12):
21         tmp=0
22         for j in range(8):
23             (R,out)=lfsr(R,mask)
24             tmp=(tmp << 1)^out   #按位异或运算符:当两对应的二进位相异时,结果为1
25         if tmp!=int(a[i],16):
26             index=0
27             break
28     if (index==1):
29         print("yes!!!")
30         print(temp)
31         break

运行结果:

 

所以flag即为flag{1110101100001101011}

方法②:

代码的大致逻辑入下:

①函数lfsr中,输出的output是R左移一位后,并且第一位(从右往左数)^lastbit的结果,而lastbite代表的是R与mask相异或后,用二进制表示的结果中1的奇偶数,1代表有奇数个1,0代表有偶数个

②函数的两个for循环中,这两个循环大致为将R不断的经过lsfr操作,每经过一次,R更新一次值,tmp的每一位代表输入的R中与mask异或的奇偶数(8个一组,写入key中)

这里的特殊性就在于,假设正在进行第19次lsfr操作,那么此时二进制R的0~18(从右往左数)为即为key[0:18],且R的最后一位为flag[0],而此次lsfr操作的输出我们已知,即output=key,lastbit=key[18],那么就可以求出R的最后一位,即flag[0]的值,往后以次类推

这里推荐两个博客,写的超详细!容易理解!

https://www.cnblogs.com/coming1890/p/13592014.html

https://www.anquanke.com/post/id/181811#h2-0

最后写出脚本如下:

 1 mask = '1010011000100011100'
 2 key='010101010011100011110111'[0:19]#十六进制表示为0x5538f7
 3 print("key:",key)
 4 
 5 R = ''
 6 for i in range(19):
 7     output = '?' + key[:18]
 8     ans = int(key[-1])^int(output[-3])^int(output[-4])^int(output[-5])^int(output[-9])^int(output[-13])^int(output[-14])^int(output[-17])
 9     R += str(ans)
10     print("R:",R)
11     key = str(ans) + key[:18]
12 
13 print((R[::-1]))
14 R = (R[::-1])
15 flag = "flag{" + R + "}"
16 print(flag)

运行结果如下:

 

posted @ 2020-10-06 15:53  jane_315  阅读(853)  评论(0编辑  收藏  举报