[CISCN2018]sm

题目展现

from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long
from Crypto.Cipher import AES
import hashlib
from random import randint
def gen512num():
    order=[]
    while len(order)!=512:
        tmp=randint(1,512)
        if tmp not in order:
            order.append(tmp)
    ps=[]
    for i in range(512):
        p=getPrime(512-order[i]+10)
        pre=bin(p)[2:][0:(512-order[i])]+"1"
        ps.append(int(pre+"0"*(512-len(pre)),2))
    return ps

def run():
    choose=getPrime(512)
    ps=gen512num()
    print "gen over"
    bchoose=bin(choose)[2:]
    r=0
    bchoose = "0"*(512-len(bchoose))+bchoose
    for i in range(512):
        if bchoose[i]=='1':
            r=r^ps[i]
    flag=open("flag","r").read()

    key=long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(),16))
    aes_obj = AES.new(key, AES.MODE_ECB)
    ef=aes_obj.encrypt(flag).encode("base64")

    open("r", "w").write(str(r))
    open("ef","w").write(ef)
    gg=""
    for p in ps:
        gg+=str(p)+"\n"
    open("ps","w").write(gg)

run()

分析题目

逐个分析一下:

先看get512num()

def gen512num():
    order=[]
    while len(order)!=512:
        tmp=randint(1,512)
        if tmp not in order:
            order.append(tmp)
    ps=[]
    for i in range(512):
        p=getPrime(512-order[i]+10)
        pre=bin(p)[2:][0:(512-order[i])]+"1"
        ps.append(int(pre+"0"*(512-len(pre)),2))
    return ps

主要是生成512个数字

虽然是通过随机数生成的,但事实上这个前缀的长度是可以反算出来的,不妨试一下

f = open("ps","r")
line = f.readlines()
s=[]
for i in range(len(line)):
    line[i] = int(line[i].strip('\n'))
    t=line[i]
    len=0
    while(t%2==0):
        len+=1
        t=t>>1
    s.append(512-len)
#s.sort() 一旦排序了就看出其中玄机了
print(s)

再看run()

def run():
    choose=getPrime(512)
    ps=gen512num()
    print "gen over"
    bchoose=bin(choose)[2:]
    r=0
    bchoose = "0"*(512-len(bchoose))+bchoose
    for i in range(512):
        if bchoose[i]=='1':
            r=r^ps[i]
    flag=open("flag","r").read()

    key=long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(),16))
    aes_obj = AES.new(key, AES.MODE_ECB)
    ef=aes_obj.encrypt(flag).encode("base64")

    open("r", "w").write(str(r))
    open("ef","w").write(ef)
    gg=""
    for p in ps:
        gg+=str(p)+"\n"
    open("ps","w").write(gg)

这里面就是看bchoose里面的数字,如果是1,那么r与ps异或,我们再看,key中需要choose,所以现在我们要做的很像逆向工程的事情,就是倒求出choose。

这是因为比如最后一个值为1的位,导致它值为1的可能,只有前缀长为它对应位置的那个数(因为前缀的结尾必为1)

异或这个数把这个数造成的影响消除,这样从后往前找可以再发现一个位仅受ps中的一个数的影响的情况,依次类推,就可以还原出 r 到底异或了哪些数,找到都是异或了哪些数,根据 r 的产生的方式,我们就能知道choose的哪些位是1。
 

f1=open("r","r")

k=int(f1.read())
m=list("0"*512) #bchoose

for i in range(511,-1,-1):
    if((k>>(511-i))%2==1):
        k=k^line[s.index(i+1)]
        m[s.index(i+1)]= "1"
    else:
        m[s.index(i+1)]="0"
choose=int("0b"+"".join(m),2)
#choose=11400599473028310480620591074112690318799501642425666449519888152497765475409346201248744734864375690112798434541063767944755958258558428437088372812844657

EXP

import base64
from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long
from Crypto.Cipher import AES
import hashlib
with open("ps", "r") as f:
    p=f.readlines()
s=[]
for i in range(len(p)):
    x=0
    a=int(p[i])
    while a%2==0:
        x+=1
        a=a>>1
    s.append(x)
#s序列表示数组中的每个数的最后有几个零
r= int(open("r","r").read())
bchoose=[0]*512
for i in range(512-1,-1,-1):
    if (r>>(511-i))%2==1:
        r=r^int(p[s.index(511-i)])
        bchoose[s.index(511-i)]=1
    else:
        bchoose[s.index(511-i)]=0
choose=''
for i in bchoose:
    choose+=str(i)
choose=int(choose,2)
key=long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(),16))
aes_obj = AES.new(key, AES.MODE_ECB)
ef = open("ef","r").read().encode()
ef=base64.b64decode(ef)
flag=aes_obj.decrypt(ef)
print(flag)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值