羊城杯2023

Easy_3L

题目

from gmpy2 import *
from Crypto.Util.number import *
from secret import flag

m = bytes_to_long(flag)


def get_key():
    p = getPrime(1400)
    f = getRandomNBitInteger(1024)
    while True:
        q = getPrime(512)
        if gcd(f, q) != 1:
            continue
        else:
            break
    h = (invert(f, p) * q) % p
    return p, h


def encrypt1(m):
    a = getPrime(250)
    b = getRandomNBitInteger(240)
    n = getPrime(512)
    seed = m
    s = [0] * 6
    s[0] = seed
    for i in range(1, 6):
        s[i] = (s[i - 1] * a + b) % n
    return s


def encrypt2(msg, p, h):
    s = getRandomNBitInteger(512)
    c = (s * h + msg) % p
    return c


s = encrypt1(m)
print("S1 =", s[1])
print("S2 =", s[2])
print("S4 =", s[4])
print("S5 =", s[5])

p, h = get_key()
c = encrypt2(s[3], p, h)
print("p =", p)
print("h =", h)
print("c =", c)

# S1 = 28572152986082018877402362001567466234043851789360735202177142484311397443337910028526704343260845684960897697228636991096551426116049875141
# S2 = 1267231041216362976881495706209012999926322160351147349200659893781191687605978675590209327810284956626443266982499935032073788984220619657447889609681888
# S4 = 9739918644806242673966205531575183334306589742344399829232076845951304871478438938119813187502023845332528267974698273405630514228632721928260463654612997
# S5 = 9755668823764800147393276745829186812540710004256163127825800861195296361046987938775181398489372822667854079119037446327498475937494635853074634666112736
# p = 25886434964719448194352673440525701654705794467884891063997131230558866479588298264578120588832128279435501897537203249743883076992668855905005985050222145380285378634993563571078034923112985724204131887907198503097115380966366598622251191576354831935118147880783949022370177789175320661630501595157946150891275992785113199863734714343650596491139321990230671901990010723398037081693145723605154355325074739107535905777351
# h = 2332673914418001018316159191702497430320194762477685969994411366563846498561222483921873160125818295447435796015251682805613716554577537183122368080760105458908517619529332931042168173262127728892648742025494771751133664547888267249802368767396121189473647263861691578834674578112521646941677994097088669110583465311980605508259404858000937372665500663077299603396786862387710064061811000146453852819607311367850587534711
# c = 20329058681057003355767546524327270876901063126285410163862577312957425318547938475645814390088863577141554443432653658287774537679738768993301095388221262144278253212238975358868925761055407920504398004143126310247822585095611305912801250788531962681592054588938446210412897150782558115114462054815460318533279921722893020563472010279486838372516063331845966834180751724227249589463408168677246991839581459878242111459287

LCG中的初始种子就是 m m m,已知 S 1 , S 2 , S 4 , S 5 S_{1},S_{2},S_{4},S_{5} S1,S2,S4,S5,要恢复初始种子,我们要先求出 S 3 S3 S3.

在函数 encrypt2(msg, p, h)中,已知 p , h , c p,h,c p,h,c.

m s g , s msg,s msg,s是未知的,考虑用格,

构造格 L L L,
( k , s , 1 ) ( − p , 0 , 0 − h , 1 , 0 c , 0 , 2 512 ) = ( m s g , s , 2 512 ) (k,s,1)\begin{pmatrix} -p ,&0,&0 \\ -h,&1,&0 \\ c,&0,&2^{512} \end{pmatrix}=(msg,s,2^{512}) (k,s,1)p,h,c,0,1,0,002512=(msg,s,2512)
m s g msg msg就是 S 3 S_{3} S3,这样就可以恢复 s e e d seed seed

from Crypto.Util.number import *

p = 25886434964719448194352673440525701654705794467884891063997131230558866479588298264578120588832128279435501897537203249743883076992668855905005985050222145380285378634993563571078034923112985724204131887907198503097115380966366598622251191576354831935118147880783949022370177789175320661630501595157946150891275992785113199863734714343650596491139321990230671901990010723398037081693145723605154355325074739107535905777351
h = 2332673914418001018316159191702497430320194762477685969994411366563846498561222483921873160125818295447435796015251682805613716554577537183122368080760105458908517619529332931042168173262127728892648742025494771751133664547888267249802368767396121189473647263861691578834674578112521646941677994097088669110583465311980605508259404858000937372665500663077299603396786862387710064061811000146453852819607311367850587534711
c = 20329058681057003355767546524327270876901063126285410163862577312957425318547938475645814390088863577141554443432653658287774537679738768993301095388221262144278253212238975358868925761055407920504398004143126310247822585095611305912801250788531962681592054588938446210412897150782558115114462054815460318533279921722893020563472010279486838372516063331845966834180751724227249589463408168677246991839581459878242111459287


mat = [[-p,0,0],[-h,1,0],[c,0,2^512]]
M = Matrix(ZZ,mat)
hh = M.LLL()[0]
# print(hh)

msg = hh[0]
print(f"msg = {msg}")

# msg = 10700695166096094995375972320865971168959897437299342068124161538902514000691034236758289037664275323635047529647532200693311709347984126070052011571264606

LCG恢复seed

import gmpy2
from Crypto.Util.number import GCD, isPrime, long_to_bytes

c=[28572152986082018877402362001567466234043851789360735202177142484311397443337910028526704343260845684960897697228636991096551426116049875141,
1267231041216362976881495706209012999926322160351147349200659893781191687605978675590209327810284956626443266982499935032073788984220619657447889609681888,
10700695166096094995375972320865971168959897437299342068124161538902514000691034236758289037664275323635047529647532200693311709347984126070052011571264606,
9739918644806242673966205531575183334306589742344399829232076845951304871478438938119813187502023845332528267974698273405630514228632721928260463654612997,
9755668823764800147393276745829186812540710004256163127825800861195296361046987938775181398489372822667854079119037446327498475937494635853074634666112736
]
# print(c[2].bit_length())
t=[]
for i in range(1,len(c)):
    t.append(c[i]-c[i-1])

m = 0                     # 求n
for i in range(1,len(t)-1):
    m = GCD(t[i+1]*t[i-1]-t[i]**2, m)
# print(isPrime(m))   # False m的倍数
print(f"n = {m}")

for i in range(1,100):
    if isPrime(m//i):
        print(i)   # i是4
        m//=i
        break

a = (c[3]-c[2])*gmpy2.invert(c[2]-c[1],m) % m
b = (c[2]-a*c[1]) % m
a_1=gmpy2.invert(a,m)
print(f"a = {a}")
print(f"b = {b}")
print(f"a_1 = {a_1}")


n = 12433235385460084327215142269091752668477278692416805859007828624838647815241707248797912107322868748847211061641608674422095027981318008221949510129177787
a = 1017579321905754831612145134014116183026524698685218523407174987842084260441
b = 1244547131344198183940330607549789182491018543684349414313485985685030480
a_1 = 11328311979915953125685402059730442802186525001769955129939835010285255328316264293029576580150862310729537002669636904024186460482729761660664512406166499

m = (c[0]-b)*a_1 % n
print(long_to_bytes(m))
b'DASCTF{NTRU_L0G_a6e_S1mpLe}'

danger_RSA

题目

from Crypto.Util.number import *

m = bytes_to_long(flag)


def get_key(a, nbit):
    assert a >= 2
    while True:
        X = getRandomInteger(nbit // a)
        s = getRandomRange(pow(2, a ** 2 - a + 4), pow(2, a ** 2 - a + 5))
        p = X ** a + s
        if isPrime(p):
            return (p, s)


p, s = get_key(a, 1024)
q, t = get_key(a, 1024)

N = p * q
e = s * t
c = pow(m, e, N)
print("N =", N)
print("e =", e)
print("c =", c)
# N = 20289788565671012003324307131062103060859990244423187333725116068731043744218295859587498278382150779775620675092152011336913225797849717782573829179765649320271927359983554162082141908877255319715400550981462988869084618816967398571437725114356308935833701495015311197958172878812521403732038749414005661189594761246154666465178024563227666440066723650451362032162000998737626370987794816660694178305939474922064726534186386488052827919792122844587807300048430756990391177266977583227470089929347969731703368720788359127837289988944365786283419724178187242169399457608505627145016468888402441344333481249304670223
# e = 11079917583
# c = 13354219204055754230025847310134936965811370208880054443449019813095522768684299807719787421318648141224402269593016895821181312342830493800652737679627324687428327297369122017160142465940412477792023917546122283870042482432790385644640286392037986185997262289003477817675380787176650410819568815448960281666117602590863047680652856789877783422272330706693947399620261349458556870056095723068536573904350085124198592111773470010262148170379730937529246069218004969402885134027857991552224816835834207152308645148250837667184968030600819179396545349582556181916861808402629154688779221034610013350165801919342549766

题目最终还是回到 R S A RSA RSA,要求解 p , q p,q p,q.这里构造了一种公钥生成方式
p = a r + s q = b r + t p=a^{r}+s\\q=b^{r}+t p=ar+sq=br+t
还知道,
e = s ∗ t e=s*t e=st
s , t s,t s,t是在一定范围里面的,思考是否可以通过分解 e e e,然后排列组合求出 s , t s,t s,t的组合。这里需要注意的是,组合有很多种,正确的组合才能求出整数解,要多尝试。

根据 e b i t e_{bit} ebit我们是可以推断出r的。

print(e.bit_length())#34
for m in range(10):
    print(m,m**2-m+4,m**2-m+5)
# 4,16,17

得到 r = 4 r=4 r=4, s b i t = 17 , t b i t = 17 s_{bit}=17,t_{bit}=17 sbit=17,tbit=17 a b i t = 256 , b b i t = 256 a_{bit}=256,b_{bit}=256 abit=256,bbit=256,所以 s , t s,t s,t的值不足以影响 N N N的大小,故有
N = p ∗ q = ( a r + s ) ( b r + t ) = ( a b ) r ⇒ a ∗ b = g m p y 2. i r o o t ( n , 4 ) N=p*q=(a^{r}+s)(b^{r}+t)=(ab)^{r}\Rightarrow a*b=gmpy2.iroot(n,4) N=pq=(ar+s)(br+t)=(ab)rab=gmpy2.iroot(n,4)
这样,就能联立方程,求出 a , b a,b a,b

验证 s b i t = t b i t s_{bit}=t_{bit} sbit=tbit是17的

tepm = [3,7,7,19,691,5741]
for e in combinations(tepm, 3):
    sum = 1
    for i in e:
        sum *= i
    if sum in range(2**16, 2**17):
        print(e)
        print(sum)

s,t = [3*7*5741,7*19*691]
# print(tepm1[0].bit_length()) 17
# print(tepm1[1].bit_length())  17

然后联立方程解 a , b a,b a,b

import gmpy2
from Crypto.Util.number import *
n = 20289788565671012003324307131062103060859990244423187333725116068731043744218295859587498278382150779775620675092152011336913225797849717782573829179765649320271927359983554162082141908877255319715400550981462988869084618816967398571437725114356308935833701495015311197958172878812521403732038749414005661189594761246154666465178024563227666440066723650451362032162000998737626370987794816660694178305939474922064726534186386488052827919792122844587807300048430756990391177266977583227470089929347969731703368720788359127837289988944365786283419724178187242169399457608505627145016468888402441344333481249304670223
e = 11079917583
c = 13354219204055754230025847310134936965811370208880054443449019813095522768684299807719787421318648141224402269593016895821181312342830493800652737679627324687428327297369122017160142465940412477792023917546122283870042482432790385644640286392037986185997262289003477817675380787176650410819568815448960281666117602590863047680652856789877783422272330706693947399620261349458556870056095723068536573904350085124198592111773470010262148170379730937529246069218004969402885134027857991552224816835834207152308645148250837667184968030600819179396545349582556181916861808402629154688779221034610013350165801919342549766

r,s=[3*7*5741,7*19*691]
ab=int(gmpy2.iroot(n,4)[0])
var('a b')
f1=a*b==ab
f2=n==(ab**4)+a**4*s+b**4*r+r*s
print(solve([f1,f2],[a,b]))

'''a = 47783641287938625512681830427927501009821495321018170621907812035456872958654
b = 44416071018427916652440592614276227563515579156219730344722242565477265479486'''

在求解私钥 d d d的时候发现 e 和 p h i e和phi ephi不互素。一般 p 和 q p和q pq很大,在 f l a g flag flag没有填充的情况下,会小于 p 和 q p和q pq,可以将 p , q p,q p,q单独作为公钥求解。

先把不互素的部分去除,然后剩下的利用 p 或 者 q p或者q pq构建多项式环求根。

a = 47783641287938625512681830427927501009821495321018170621907812035456872958654
b = 44416071018427916652440592614276227563515579156219730344722242565477265479486
p=a**4+r
q=b**4+s
# print(gcd(e,p-1) #3
d=inverse(e//3,p-1)
m_3=pow(c,d,n)
e=3
P.<a>=PolynomialRing(Zmod(p),implementation='NTL')
f=a^e-m_3
mps=f.monic().roots()
for i in mps:
    flag=long_to_bytes(int(i[0]))
    if b'DASCTF' in flag or b'dasctf' in flag:
        print(flag)
b'DASCTF{C0nsTruct!n9_Techn1qUe2_f0r_RSA_Pr1me_EnC2ypt10N}'

赛后尝试用 q q q作为公钥来求解。发现有一个问题就是去除公因数不彻底,逆元不存在

gcd_eq_1 = gmpy2.gcd(e,q-1) # 7
gcd_2 = gmpy2.gcd(e//7,q-1) # 7

所以为了一次性把公因数去除彻底,让 e e e q − 1 q-1 q1都除以最大公因数,而不应该只让一边去除公因数(只让 e / / 7 e//7 e//7).

import gmpy2
from Crypto.Util.number import *
n = 
e = 
c = 
#print(e.bit_length())  34

# for m in range(10):
#     print(m,m**2-m+4,m**2-m+5)
#4,16,17
r,s=[3*7*5741,7*19*691]
ab=int(gmpy2.iroot(n,4)[0])
# var('a b')
# f1=a*b==ab
# f2=n==(ab**4)+a**4*s+b**4*r+r*s
# print(solve([f1,f2],[a,b]))
a = 47783641287938625512681830427927501009821495321018170621907812035456872958654
b = 44416071018427916652440592614276227563515579156219730344722242565477265479486
p=a**4+r
q=b**4+s
# print(gcd(e//7,q-1)
d = inverse(e//7,(q-1)//7) 
# d = inverse(e//49,q-1)
m_49 = pow(c,d,n)
e = 7  
# e = 49
q.<a>=PolynomialRing(Zmod(q),implementation='NTL')
f = a^e-m_49
mps = f.monic().roots()
for i in mps:
    flag=long_to_bytes(int(i[0]))
    if b'DASCTF' in flag or b'dasctf' in flag:
        print(flag)

XOR贯穿始终

第一次做这样的题,卡了很久很久。。

题目附件是加密的,要想办法拿到密码。注意到题目给了一个 m a s s e g e . t x t massege.txt massege.txt,打开一看,是社会主义核心价值观编码,在线网站解密就行。

massege.txt的内容:

自由和谐和谐富强公正友善爱国公正法治法治文明和谐自由法治自由法治平等公正友善公正公正民主法治自由公正敬业和谐富强公正友善爱国和谐平等平等友善敬业法治敬业和谐富强法治平等平等友善敬业公正公正公正友善敬业法治平等平等诚信自由公正自由平等友善敬业公正友善法治和谐和谐

解码:

C0ngr4tulati0n5_y0u_fou^d_m3

现在就能打开附件了,看到题目

from gmpy2 import gcd
from Crypto.Util.number import getPrime
from secret import enflag

p = getPrime(512)
q = getPrime(512)
n = q * p
phi = (p - 1) * (q - 1)
e = getPrime(17)
assert gcd(e, phi) == 1
# 以上信息生成了私钥文件,但文件被损坏了你能提取有用信息吗

c = pow(enflag, e, n)
print('c = ' + str(c))

'''
c = 91817924748361493215143897386603397612753451291462468066632608541316135642691873237492166541761504834463859351830616117238028454453831120079998631107520871612398404926417683282285787231775479511469825932022611941912754602165499500350038397852503264709127650106856760043956604644700201911063515109074933378818
'''

私钥丢失了,但是给了一个 p e m pem pem证书,要我们从中提取私钥。

-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALmtMy+2uH1ZtbIL
SuiAukFthyQRH5mp7UmLyzZQkdg9zEP9/5tgffikQ7ytx5kHySHnazgAO1sOzmYE
N4Axlev6uafiP8B1Eij97v5VkYJ1I9e3mtBNheTbXKoT8op+ASQ1fQaF4A8UzLuW
eZeZI8JTH/SH+bolAK3kiZXDFdkTAgMBAAECgYEAl067LaC7Cvs2A5cMPhfYsESv
IgcKN1CwW4Sd3u8dSphhgu7TgyzIuvwxbuo2g1BC6WwKhaI6vGN+csfw6nh98GEn
/p3D0huNroAYvf/DRRB9UnHdttX7wB+Mv3P0RBDWHgBiCDVvHFuFUV78cIs0tnbn
jxjU07aPV2XRC3AfA2ECQQDqWUNPVg3i6vTyHCL7EGkbeUheYpAAfcKCQrxjc5+5
X6A+XtgHAA1JHwykPlCpHUOmlA85DJF1ejuoImzlgRLJAkEAytTCnQF+MN2r1gaA
UETZyj5qMYT7Th8zKEVVVJjDawLnuX4usJ2FyRnjCkk86U75QSJhw5mMc0QnG25u
Gz3++w==
-----END PRIVATE KEY-----

先了解一下 p e m pem pem证书吧

RSA公私钥结构

(PKCS1) RSA 公私钥 pem 文件解析

非对称加密RSA公私钥pkcs1与pkcs8格式的转换

pem证书的base64解码,再转hex,分割出 n , e , d , p , q n,e,d,p,q n,e,d,p,q.在这里 d p , d q , m p d_{p},d_{q},m_{p} dp,dq,mp是没有泄露的。

在这里插入图片描述

最后发现真正的 f l a g flag flag R S A RSA RSA f l a g flag flag和社会主义核心价值观解码结果异或的结果。

import gmpy2
from Crypto.Util.number import long_to_bytes, bytes_to_long

# s = '00 ca d4 c2 9d 01 7e 30 dd ab d6 06 80 50 44 d9 ca 3e 6a 31 84 fb 4e 1f 33 28 45 55 54 98 c3 6b 02 e7 b9 7e 2e b0 9d 85 c9 19 e3 0a 49 3c e9 4e f9 41 22 61 c3 99 8c 73 44 27 1b 6e 6e 1b 3d fe fb'
# n1 = ''
# sum = 0
# for i in s:
#     if i != ' ':
#         n1 += i
#     else:
#         sum += 1
# print(n1)
# print(sum)
n = 0x00b9ad332fb6b87d59b5b20b4ae880ba416d8724111f99a9ed498bcb365091d83dcc43fdff9b607df8a443bcadc79907c921e76b38003b5b0ece660437803195ebfab9a7e23fc0751228fdeefe5591827523d7b79ad04d85e4db5caa13f28a7e0124357d0685e00f14ccbb9679979923c2531ff487f9ba2500ade48995c315d9130203010001
d = 0x00974ebb2da0bb0afb3603970c3e17d8b044af22070a3750b05b849ddeef1d4a986182eed3832cc8bafc316eea36835042e96c0a85a23abc637e72c7f0ea787df06127fe9dc3d21b8dae8018bdffc345107d5271ddb6d5fbc01f8cbf73f44410d61e006208356f1c5b85515efc708b34b676e78f18d4d3b68f5765d10b701f0361
p = 0x00ea59434f560de2eaf4f21c22fb10691b79485e6290007dc28242bc63739fb95fa03e5ed807000d491f0ca43e50a91d43a6940f390c91757a3ba8226ce58112c9
q = 0x00cad4c29d017e30ddabd606805044d9ca3e6a3184fb4e1f332845555498c36b02e7b97e2eb09d85c919e30a493ce94ef9412261c3998c7344271b6e6e1b3dfefb
e = 0x010001
c = 91817924748361493215143897386603397612753451291462468066632608541316135642691873237492166541761504834463859351830616117238028454453831120079998631107520871612398404926417683282285787231775479511469825932022611941912754602165499500350038397852503264709127650106856760043956604644700201911063515109074933378818
# print(p*q == n)

# print(p == p1)
# print(q == q1)
m = gmpy2.powmod(c,d,p*q)
m1 = b'C0ngr4tulati0n5_y0u_fou^d_m3'
print(long_to_bytes(m^bytes_to_long(m1)))

esyRSA

题目

from gmpy2 import invert
from md5 import md5
from secret import p, q

e = ?????
n = p*q
phi = (p-1)*(q-1)
d = invert(e, phi)
ans = gcd(e,phi)

print n, e, d
print "Flag: DASCTF{%s}" %md5(str(p + q)).hexdigest()

"""
n = 8064259277274639864655809758868795854117113170423331934498023294296505063511386001711751916634810056911517464467899780578338013011453082479880809823762824723657495915284790349150975180933698827382368698861967973964030509129133021116919437755292590841218316278732797712538885232908975173746394816520256585937380642592772746398646558097588687958541171131704233319344980232942965050635113860017117519166348100569115174644678997805783380130114530824798808098237628247236574959152847903491509751809336988273823686988619679739640305091291330211169194377552925908412183162787327977125388852329089751737463948165202565859373
d = 14218766449983537783699024084862960813708451888387858392014856544340557703876299258990323621963898510226357248200187173211121827541826897886277531706124228848229095880229718049075745233893843373402201077890407507625110061976931591596708901741146750809962128820611844426759462132623616118530705745098783140913
"""

题目分析:就是一个 R S A RSA RSA,给了 n , d n,d n,d但是需要恢复 e e e,最后让我们求的是 p + q p+q p+q.这里需要注意的是, n n n重复了一遍,去除重复部分才是真正的 n n n.

n = 80642592772746398646558097588687958541171131704233319344980232942965050635113860017117519166348100569115174644678997805783380130114530824798808098237628247236574959152847903491509751809336988273823686988619679739640305091291330211169194377552925908412183162787327977125388852329089751737463948165202565859373

格求解

比赛的时候我其实是想过用格来解决的,但是我尝试失败了。之后看到一个师傅的 W P WP WP就是用格来求解的。
e ∗ d ≡ 1   m o d   p h i ⇒ e*d\equiv1\textbf{ }mod\textbf{ }phi\Rightarrow ed1 mod phi

e ∗ d = k ∗ n − k ∗ ( p + q − 1 ) + 1 e*d=k*n-k*(p+q-1)+1 ed=knk(p+q1)+1

( k , e ) ( n , 0 − d , 2 512 ) = ( k ∗ ( p + q − 1 ) − 1 , e ∗ 2 512 ) (k,e)\begin{pmatrix} n, &0 \\ -d,&2^{512} \end{pmatrix}=(k*(p+q-1)-1,e*2^{512}) (k,e)(n,d,02512)=(k(p+q1)1,e2512)

这里解释一下为什么要这样构造格。

p , q p,q p,q都是 512 b i t 512bit 512bit,而 e e e是比较小的(只有5位数),为了使目标向量的两个坐标差不多大,让 e e e乘上 2 512 2^{512} 2512.(我当时就是没乘。。)

from hashlib import md5
import gmpy2
d = 14218766449983537783699024084862960813708451888387858392014856544340557703876299258990323621963898510226357248200187173211121827541826897886277531706124228848229095880229718049075745233893843373402201077890407507625110061976931591596708901741146750809962128820611844426759462132623616118530705745098783140913
n = 80642592772746398646558097588687958541171131704233319344980232942965050635113860017117519166348100569115174644678997805783380130114530824798808098237628247236574959152847903491509751809336988273823686988619679739640305091291330211169194377552925908412183162787327977125388852329089751737463948165202565859373
m = matrix(ZZ, [[n,0],[-d,2**512]])
k_pq, e = m.LLL()[0]
print("k_pq = ", k_pq)
print("e = ",e)
k_pq = abs(k_pq)
e = abs(e)//2**512
k = gcd(e*d-1,k_pq+1)
p_q = (k_pq+1)//k+1
print("Flag: DASCTF{%s}" %md5(str(p_q).encode()).hexdigest())
Flag: DASCTF{4ae33bea90f030bfddb7ac4d9222ef8f}

连分数求解

e ∗ d ≡ 1   m o d   p h i ⇒ e*d\equiv1\textbf{ }mod\textbf{ }phi\Rightarrow ed1 mod phi

e ∗ d = k ∗ p h i + 1 ⇒ e*d=k*phi+1\Rightarrow ed=kphi+1

同 除 以 e ∗ p h i , d p h i = k e + 1 e ∗ p h i 同除以e*phi,\frac{d}{phi}=\frac{k}{e}+\frac{1}{e*phi} ephi,phid=ek+ephi1

因 为 p h i ≈ n , 且 p h i 非 常 大 , 所 以 1 e ∗ p h i ≈ 0 因为phi\approx n,且phi非常大,所以\frac{1}{e*phi}\approx 0 phin,phi,ephi10

所 以 , d n = k e + 1 e ∗ p h i 所以,\frac{d}{n}=\frac{k}{e}+\frac{1}{e*phi} nd=ek+ephi1

也 就 是 说 , d n ≈ k e 也就是说,\frac{d}{n}\approx\frac{k}{e} ndek

而 d , n 是 已 知 的 , 所 以 对 d n 进 行 连 分 数 展 开 , 得 到 的 一 串 分 母 很 有 可 能 就 是 e 而d,n是已知的,所以对\frac{d}{n}进行连分数展开,得到的一串分母很有可能就是e d,nnde

# sage
from hashlib import md5

n = 80642592772746398646558097588687958541171131704233319344980232942965050635113860017117519166348100569115174644678997805783380130114530824798808098237628247236574959152847903491509751809336988273823686988619679739640305091291330211169194377552925908412183162787327977125388852329089751737463948165202565859373
d = 14218766449983537783699024084862960813708451888387858392014856544340557703876299258990323621963898510226357248200187173211121827541826897886277531706124228848229095880229718049075745233893843373402201077890407507625110061976931591596708901741146750809962128820611844426759462132623616118530705745098783140913

c = continued_fraction(d/n)  # 计算连分数
i = 1
while True:
    k = c.numerator(i)  # 分子
    e = c.denominator(i) # 分母
    if (e*d-1) % k == 0 and len(str(e)) == 5:
        print(k)
        print(e)
        p_q = n+1-(e*d-1)//k
        break
    i+=1
print(f"p+q = {p_q}")
print("Flag: DASCTF{%s}" %md5(str(p_q).encode()).hexdigest())
Flag: DASCTF{4ae33bea90f030bfddb7ac4d9222ef8f}

维纳攻击求解

维纳攻击的核心就在于连分数,所以用维纳攻击的脚本来求解也是可以的。

参考:CTF-RSA_维纳攻击脚本

from hashlib import md5

import gmpy2
import libnum

def continuedFra(x, y):
    """计算连分数
    :param x: 分子
    :param y: 分母
    :return: 连分数列表
    """
    cf = []
    while y:
        cf.append(x // y)
        x, y = y, x % y
    return cf
def gradualFra(cf):
    """计算传入列表最后的渐进分数
    :param cf: 连分数列表
    :return: 该列表最后的渐近分数
    """
    numerator = 0
    denominator = 1
    for x in cf[::-1]:
        # 这里的渐进分数分子分母要分开
        numerator, denominator = denominator, x * denominator + numerator
    return numerator, denominator
def solve_pq(a, b, c):
    """使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
    :param a:x^2的系数
    :param b:x的系数
    :param c:pq
    :return:p,q
    """
    par = gmpy2.isqrt(b * b - 4 * a * c)
    return (-b + par) // (2 * a), (-b - par) // (2 * a)
def getGradualFra(cf):
    """计算列表所有的渐近分数
    :param cf: 连分数列表
    :return: 该列表所有的渐近分数
    """
    gf = []
    for i in range(1, len(cf) + 1):
        gf.append(gradualFra(cf[:i]))
    return gf


def wienerAttack(d, n):
    """
    :param d:
    :param n:
    :return: e
    """
    cf = continuedFra(d, n)
    gf = getGradualFra(cf)
    for e, k in gf:
        if k == 0:
            continue
        if (e * d - 1) % k != 0:
            continue
        phi = (e * d - 1) // k
        # p, q = solve_pq(1, n - phi + 1, n)
        if len(str(e))==5:
            p_q = n+1-(e*d-1)//k
            return e,p_q


n = 80642592772746398646558097588687958541171131704233319344980232942965050635113860017117519166348100569115174644678997805783380130114530824798808098237628247236574959152847903491509751809336988273823686988619679739640305091291330211169194377552925908412183162787327977125388852329089751737463948165202565859373
d = 14218766449983537783699024084862960813708451888387858392014856544340557703876299258990323621963898510226357248200187173211121827541826897886277531706124228848229095880229718049075745233893843373402201077890407507625110061976931591596708901741146750809962128820611844426759462132623616118530705745098783140913

e,p_q = wienerAttack(d, n)
print(f"e = {e}")

print("Flag: DASCTF{%s}" %md5(str(p_q).encode()).hexdigest())

xenny库

值得一提的是,我们也可以直接用 X e n n y 库 Xenny库 Xenny里面的 w i e n e r 函 数 wiener函数 wiener解决 x e n n y 师 傅 xenny师傅 xenny真的泰裤辣!!

from hashlib import md5
from xenny.ctf.crypto.modern.asymmetric.rsa import wiener
n = 80642592772746398646558097588687958541171131704233319344980232942965050635113860017117519166348100569115174644678997805783380130114530824798808098237628247236574959152847903491509751809336988273823686988619679739640305091291330211169194377552925908412183162787327977125388852329089751737463948165202565859373
d = 14218766449983537783699024084862960813708451888387858392014856544340557703876299258990323621963898510226357248200187173211121827541826897886277531706124228848229095880229718049075745233893843373402201077890407507625110061976931591596708901741146750809962128820611844426759462132623616118530705745098783140913

e,p,q = wiener.attack(n, d)
print(f"e = {e}")
print("p = ",p)
print("q = ",q)
print("Flag: DASCTF{%s}" %md5(str(p + q).encode()).hexdigest())
e = 13521
p =  10181341212828413853336916619161138854377885230386496425058202154486415709366161346816273366144505351043947477469664133317598479763451392984403646602585037
q =  7920625690369490250766357750388349704260128405941822835255851274284409978206593795103040446837018619894098452542488850045009467407103749792461438242280929
Flag: DASCTF{4ae33bea90f030bfddb7ac4d9222ef8f}

MCeorpkpleer

题目

from Crypto.Util.number import *
from secret import flag


def pubkey(list, m, w):
    pubkey_list = []
    for i in range(len(e_bin)):
        pubkey_list.append(w * list[i] % m)
    return pubkey_list


def e_cry(e, pubkey):
    pubkey_list = pubkey
    encode = 0
    for i in range(len(e)):
        encode += pubkey_list[i] * int(e[i]) % m
    return encode


p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = getPrime(64)
m = bytes_to_long(flag)
c = pow(m, e, n)

e_bin = (bin(e))[2:]
list = [pow(3, i) for i in range(len(e_bin))]
m = getPrime(len(bin(sum(list))) - 1)
w = getPrime(64)
pubkey = pubkey(list, m, w)
en_e = e_cry(e_bin, pubkey)

print('p = {}\n'
      'n = {}\n'
      'c = {}\n'
      'pubkey = {}\n'
      'en_e = {}'.format((p >> 435) << 435, n, c, pubkey, en_e))

'''
p = 139540788452365306201344680691061363403552933527922544113532931871057569249632300961012384092481349965600565669315386312075890938848151802133991344036696488204791984307057923179655351110456639347861739783538289295071556484465877192913103980697449775104351723521120185802327587352171892429135110880845830815744
n = 22687275367292715121023165106670108853938361902298846206862771935407158965874027802803638281495587478289987884478175402963651345721058971675312390474130344896656045501040131613951749912121302307319667377206302623735461295814304029815569792081676250351680394603150988291840152045153821466137945680377288968814340125983972875343193067740301088120701811835603840224481300390881804176310419837493233326574694092344562954466888826931087463507145512465506577802975542167456635224555763956520133324723112741833090389521889638959417580386320644108693480886579608925996338215190459826993010122431767343984393826487197759618771
c = 156879727064293983713540449709354153986555741467040286464656817265584766312996642691830194777204718013294370729900795379967954637233360644687807499775502507899321601376211142933572536311131955278039722631021587570212889988642265055045777870448827343999745781892044969377246509539272350727171791700388478710290244365826497917791913803035343900620641430005143841479362493138179077146820182826098057144121231954895739989984846588790277051812053349488382941698352320246217038444944941841831556417341663611407424355426767987304941762716818718024107781873815837487744195004393262412593608463400216124753724777502286239464
pubkey = [18143710780782459577, 54431132342347378731, 163293397027042136193, 489880191081126408579, 1469640573243379225737, 4408921719730137677211, 13226765159190413031633, 39680295477571239094899, 119040886432713717284697, 357122659298141151854091, 1071367977894423455562273, 3214103933683270366686819, 9642311801049811100060457, 28926935403149433300181371, 86780806209448299900544113, 260342418628344899701632339, 781027255885034699104897017, 2343081767655104097314691051, 7029245302965312291944073153, 21087735908895936875832219459, 63263207726687810627496658377, 189789623180063431882489975131, 569368869540190295647469925393, 1708106608620570886942409776179, 601827224419797931380408071500, 1805481673259393794141224214500, 893952418336266652976851386463, 2681857255008799958930554159389, 3523079163584485147344841221130, 1524252287869625983140881149316, 50264262166963219975822190911, 150792786500889659927466572733, 452378359502668979782399718199, 1357135078508006939347199154597, 4071405235524020818041597463791, 3169230503688232995231149877299, 462706308180869526799807117823, 1388118924542608580399421353469, 4164356773627825741198264060407, 3448085117999647764701149667147, 1299270151115113835209806487367, 3897810453345341505629419462101, 2648446157152195057994615872229, 3422845870014670444537026359650, 1223552407160181874717436564876, 3670657221480545624152309694628, 1966986461557807413563286569810, 1378466783231507511243038452393, 4135400349694522533729115357179, 3361215846199738142293703557463, 1038662335715384967987468158315, 3115987007146154903962404474945, 302975818554635252993570910761, 908927455663905758980712732283, 2726782366991717276942138196849, 3657854499533237101379593333510, 1928578295715881845245137486456, 1263242285705730806288591202331, 3789726857117192418865773606993, 2324195368467747797703678306905, 2450093503961328663664213663678, 2827787910442071261545819733997, 3960871129884299055190637944954, 2837628186769067706678271320788]
en_e = 31087054322877663244023458448558
'''

题目分析:

梳理一下已知信息:
p 高 位 , n , c , e n e p高位,n,c,en_e pn,c,ene

e 是 64 b i t , 那 么 列 表 l i s t 也 是 已 知 的 e是64bit,那么列表list也是已知的 e64bit,list

未知:
q , e q,e q,e
给出 p p p高位,copper就可以恢复 p p p.继而就能知道 q q q

列表 l i s t list list的值是已知的,且 l i s t [ 0 ] list[0] list[0]就是 W W W.
p u b k e y [ i ] = w ∗ l i s t [ i ]   m o d   m ⇒ pubkey[i]=w*list[i]\textbf{ }mod\textbf{ }m\Rightarrow pubkey[i]=wlist[i] mod m

k ∗ m = p u b k e y [ i ] − w ∗ l i s t [ i ] k*m=pubkey[i]-w*list[i] km=pubkey[i]wlist[i]

求出多组 k ∗ m k*m km,这些 k ∗ m k*m km的最大公因数就是 m m m

再看 l i s t list list是一个超递增序列,直接用贪心算法求解 e e e
e n c o d e = ( p u b k e y [ 0 ] ∗ e [ 0 ] + p u b k e y [ 1 ] ∗ e [ 1 ] + . . . + p u b k e y [ 63 ] ∗ e [ 63 ] )   m o d   m encode=(pubkey[0]*e[0]+pubkey[1]*e[1]+...+pubkey[63]*e[63])\textbf{ }mod\textbf{ }m encode=(pubkey[0]e[0]+pubkey[1]e[1]+...+pubkey[63]e[63]) mod m

= ( ( w ∗ l i s t [ 0 ] ) ∗ e [ 0 ] + ( w ∗ l i s t [ 1 ] ) ∗ e [ 1 ] + . . . + ( w ∗ l i s t [ 63 ] ) ∗ e [ 63 ] )   m o d   m =((w*list[0])*e[0]+(w*list[1])*e[1]+...+(w*list[63])*e[63])\textbf{ }mod\textbf{ }m =((wlist[0])e[0]+(wlist[1])e[1]+...+(wlist[63])e[63]) mod m

= w ∗ ( l i s t [ 0 ] ∗ e [ 0 ] + l i s t [ 1 ] ∗ e [ 1 ] + . . . + l i s t [ 63 ] ∗ e [ 63 ] )   m o d   m =w*(list[0]*e[0]+list[1]*e[1]+...+list[63]*e[63])\textbf{ }mod \textbf{ }m =w(list[0]e[0]+list[1]e[1]+...+list[63]e[63]) mod m

所 以 , e n c o d e ∗ w − 1 = ( l i s t [ 0 ] ∗ e [ 0 ] + l i s t [ 1 ] ∗ e [ 1 ] + . . . + l i s t [ 63 ] ∗ e [ 63 ] )   m o d   m 所以,encode*w^{-1}=(list[0]*e[0]+list[1]*e[1]+...+list[63]*e[63])\textbf{ }mod \textbf{ }m encodew1=(list[0]e[0]+list[1]e[1]+...+list[63]e[63]) mod m

copper p p p

#sage
from Crypto.Util.number import *
hint = 139540788452365306201344680691061363403552933527922544113532931871057569249632300961012384092481349965600565669315386312075890938848151802133991344036696488204791984307057923179655351110456639347861739783538289295071556484465877192913103980697449775104351723521120185802327587352171892429135110880845830815744
n = 22687275367292715121023165106670108853938361902298846206862771935407158965874027802803638281495587478289987884478175402963651345721058971675312390474130344896656045501040131613951749912121302307319667377206302623735461295814304029815569792081676250351680394603150988291840152045153821466137945680377288968814340125983972875343193067740301088120701811835603840224481300390881804176310419837493233326574694092344562954466888826931087463507145512465506577802975542167456635224555763956520133324723112741833090389521889638959417580386320644108693480886579608925996338215190459826993010122431767343984393826487197759618771

p_high = hint
# print(p_high)
PR.<x> = PolynomialRing(Zmod(n))
f = p_high + x
x = f.small_roots(X=2^435, beta=0.4)[0]
p = p_high+int(x)
q = n//p
print(f"p = {p}")
print(f"q = {q}")
p = 139540788452365306201344680691061363403552933527922544113532931871057569249632300961012384092481349965600565669315386312075890938848151802133991344036696488204791984307057923179677630589032444985150800881889090713797496239571291907818169058929859395965304623825442220206712660451198754072531986630133689525911
q = 162585259972480477964240855936099163585362299488578311068842002571891718764319834825730036484383081273549236661473286892739224906812137330941622699836239606393084030874487072527724286268715004074797344316619876830720445250395986443767703356842297999006344406006724963545062388183647988548800359369190326996261

m m m

list = [pow(3, i) for i in range(64)]
w = pubkey[0]
m_len = len(bin(sum(list))) - 1
k_m = []
for i in range(63,50,-1):
    k_m.append(pubkey[i]-w*list[i])
m = k_m[0]
for num in k_m[1:]:
    m = gmpy2.gcd(m,num)
    if isPrime(m) and m.bit_length() == m_len:
        print("m =",m)
        break
        
# m = 4522492601441914729446821257037

贪心算法求解e

S = en_e * gmpy2.invert(w,m)
M = list
e = ''
for i in range(len(M)-1,-1,-1):
    if S >= M[i]:
        e += '1'
        S -= M[i]
    else:
        e += '0'
e = int(e[::-1],2)
print("e =",e)

# e = 15960663600754919507

最后解RSA

import gmpy2
from Crypto.Util.number import isPrime, long_to_bytes

p = 139540788452365306201344680691061363403552933527922544113532931871057569249632300961012384092481349965600565669315386312075890938848151802133991344036696488204791984307057923179677630589032444985150800881889090713797496239571291907818169058929859395965304623825442220206712660451198754072531986630133689525911
q = 162585259972480477964240855936099163585362299488578311068842002571891718764319834825730036484383081273549236661473286892739224906812137330941622699836239606393084030874487072527724286268715004074797344316619876830720445250395986443767703356842297999006344406006724963545062388183647988548800359369190326996261
c = 156879727064293983713540449709354153986555741467040286464656817265584766312996642691830194777204718013294370729900795379967954637233360644687807499775502507899321601376211142933572536311131955278039722631021587570212889988642265055045777870448827343999745781892044969377246509539272350727171791700388478710290244365826497917791913803035343900620641430005143841479362493138179077146820182826098057144121231954895739989984846588790277051812053349488382941698352320246217038444944941841831556417341663611407424355426767987304941762716818718024107781873815837487744195004393262412593608463400216124753724777502286239464

e = 15960663600754919507
d = gmpy2.invert(e,(p-1)*(q-1))
real_m = gmpy2.powmod(c,d,p*q)
print(long_to_bytes(real_m))
DASCTF{T81I_tPPS_6r7g_xlPi_OO3M_6vyV_Rkba}

SigninCrypto

题目

from random import *
from Crypto.Util.number import *
from Crypto.Cipher import DES3
from flag import flag
from key import key
from iv import iv
import os
import hashlib
import secrets

K1= key
hint1 = os.urandom(2) * 8
xor =bytes_to_long(hint1)^bytes_to_long(K1)
print(xor)

def Rand():
    rseed = secrets.randbits(1024)
    List1 = []
    List2 = []
    seed(rseed)
    for i in range(624):
        rand16 = getrandbits(16)
        List1.append(rand16)
    seed(rseed)
    for i in range(312):
        rand64 = getrandbits(64)
        List2.append(rand64)
    with open("task.txt", "w") as file:
        for rand16 in List1:
            file.write(hex(rand16)+ "\n")
        for rand64 in List2:
            file.write(hex((rand64 & 0xffff) | ((rand64 >> 32) & 0xffff) << 16) + "\n")  #拼接
Rand()

K2 = long_to_bytes(getrandbits(64))
K3 = flag[:8]

KEY = K1 + K2 + K3

IV=iv

IV1=IV[:len(IV)//2]
IV2=IV[len(IV)//2:]

digest1 = hashlib.sha512(IV1).digest().hex()
digest2 = hashlib.sha512(IV2).digest().hex()

digest=digest1+digest2 # type(digest)=='str'
hint2=(bytes_to_long(IV)<<32)^bytes_to_long(os.urandom(8))
print(hex(bytes_to_long((digest.encode()))))
print(hint2)


mode = DES3.MODE_CBC
des3 = DES3.new(KEY, mode, IV)

pad_len = 8 - len(flag) % 8
padding = bytes([pad_len]) * pad_len
flag += padding

cipher = des3.encrypt(flag)

ciphertext=cipher.hex()
print(ciphertext)

# 334648638865560142973669981316964458403
# 0x62343937373634656339396239663236643437363738396663393438316230353665353733303939613830616662663633326463626431643139323130616333363363326631363235313661656632636265396134336361623833636165373964343533666537663934646239396462323666316236396232303539336438336234393737363465633939623966323664343736373839666339343831623035366535373330393961383061666266363332646362643164313932313061633336336332663136323531366165663263626539613433636162383363616537396434353366653766393464623939646232366631623639623230353933643833
# 22078953819177294945130027344
# a6546bd93bced0a8533a5039545a54d1fee647007df106612ba643ffae850e201e711f6e193f15d2124ab23b250bd6e1

分析一下题目,这个题重点是求KEYIV.

KEY被分成了3段,IV被分成了2段.

先看一下 K 1 K1 K1.

K1= key
hint1 = os.urandom(2) * 8  
xor =bytes_to_long(hint1)^bytes_to_long(K1)
print(xor)

这里解释一下os.urandom(2)是生成2字节的随机数,os.urandom(2) * 8将这个随机数重复8遍.

我们输出一下,

xor = 334648638865560142973669981316964458403
print(long_to_bytes(xor))

发现有一些片段是重复的.
在这里插入图片描述

猜测 h i n t 1 hint1 hint1就是 b'\xfb\xc2*8,那么 K 1 K1 K1就可以求出来了.

xor = 334648638865560142973669981316964458403
print(long_to_bytes(xor))
hint1 = b'\xfb\xc2'*8
print(long_to_bytes(bytes_to_long(hint1)^xor))
# b'dasctfda'

接着我们看 K 3 K3 K3

K3 = flag[:8]

K 3 K3 K3前7位就是 b'DASCTF{',第8位需要爆破一下.

再来看 I V IV IV

IV=iv

IV1=IV[:len(IV)//2]
IV2=IV[len(IV)//2:]

digest1 = hashlib.sha512(IV1).digest().hex()
digest2 = hashlib.sha512(IV2).digest().hex()

digest=digest1+digest2 # type(digest)=='str'
hint2=(bytes_to_long(IV)<<32)^bytes_to_long(os.urandom(8))
print(hex(bytes_to_long((digest.encode()))))
print(hint2)

(bytes_to_long(IV)<<32)说明在异或的时候,IV的高32位是不受影响的,所以我们直接打印出 long_to_bytes(hint2)

,得到IV1.

hint2 = 22078953819177294945130027344
print(long_to_bytes(hint2))
# b'GWHT\xd9\x13\xbc\xcdu~\x99P'
IV1 = b'GWHT'

这里特别强调一下,digest的类型是str.(打印一下 t y p e ( d i g e s t ) type(digest) type(digest)),那么知道IV1diegest_encode是可以求出IV2的.

digest_encode = 0x62343937373634656339396239663236643437363738396663393438316230353665353733303939613830616662663633326463626431643139323130616333363363326631363235313661656632636265396134336361623833636165373964343533666537663934646239396462323666316236396232303539336438336234393737363465633939623966323664343736373839666339343831623035366535373330393961383061666266363332646362643164313932313061633336336332663136323531366165663263626539613433636162383363616537396434353366653766393464623939646232366631623639623230353933643833
digest1 = hashlib.sha512(IV1).digest().hex()
digest11 = bytes_to_long(digest1.encode())
# print(digest11.bit_length()) 1023
digest2 = digest_encode-(digest11<<1024)
print(digest2==digest11) # True

说明IV2也是 b'GWHT'.至此,IV求出,IV=b'GWHTGWHT'(这里讲一下,细心的人可能发现digest是两个相同的数拼接而成,所以IV2==IV1

最后求K2,生成K2的方法是梅森旋转法

Mersenne Twister算法有一个内部状态数据库,该数据库由624个32位无符号整数组成。在每次请求随机修改数字时,它都会提取并返回一个值,同时内部状态认为下次请求准备好。当算法消耗了所有 624 个状态之后,它会进行一次称为“扭曲”的操作,更新所有 624 个状态。

使用相同的种子对随机数生成器进行初始化,那么生成的随机数序列将是确定的,并且每次都是相同的。

再看一下随机数的生成机制:

通过生成32bit的数进行拼接或者移位(大佬教的)

如果要生成16bit的数,是先生成 32 b i t 32bit 32bit再取这个数的高16位

如果要生成64bit的数,先生成两个32bit的数x1,x2,再进行拼接x1|x2

有了这些知识,我们就可以恢复初始状态,并预测下一个随机数(也就是 K 2 K2 K2).

Rand()中,通过List1可以得到624个 32 b i t 32bit 32bit的随机数的高16位.通过 L i s t 2 List2 List2可以得到 624 624 624 32 b i t 32bit 32bit的随机数的低 16 16 16位,这样初始状态就恢复了.

exp

from Crypto.Cipher import DES3
from Crypto.Util.number import *
from randcrack import RandCrack


f = open("task.txt","r")
List = []
for data in f:
    List.append(eval(data))


List1 = List[:624] # 高16位
List2 = List[624:]
List3 = []

# 低16位
for data in List2:
    List3.append(data & 0xffff)
    List3.append((data>>16) & 0xffff)

# 恢复初始624个32bit的随机数
rc = []
for data1,data2 in zip(List1,List3):
    rc.append((data1<<16)+data2)
# print(rc)

rc1 = RandCrack()
for i in rc:
    rc1.submit(i)
K2 = long_to_bytes(rc1.predict_getrandbits(64))
# print(K2)

IV1 = b'GWHT'
IV2 = IV1
IV = IV1+IV2
K3_7 = b'DASCTF{'
K1 = b'dasctfda'
c = 'a6546bd93bced0a8533a5039545a54d1fee647007df106612ba643ffae850e201e711f6e193f15d2124ab23b250bd6e1'
c = bytes.fromhex(c)
for i in range(2**10):
    KEY = K1 + K2 + K3_7 + long_to_bytes(i)
    mode = DES3.MODE_CBC
    des3 = DES3.new(KEY, mode, IV)
    m = des3.decrypt(c)
    if b'DASCTF' in m:
        print(m)
        break
DASCTF{8e5ee461-f4e1-4af2-8632-c9d62f4dc073}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值