CTF中RSA基础题型实战解析:从入门到精通

1. RSA基础与CTF解题入门

RSA加密算法自1977年问世以来,一直是现代密码学的基石之一。在CTF竞赛中,RSA相关题目占据了密码学领域的半壁江山。我第一次接触CTF中的RSA题目时,完全被那些大整数和模运算搞晕了,直到后来才发现这些题目其实都有规律可循。

RSA的核心原理基于大整数分解的困难性。简单来说,就是选择两个大质数p和q,计算n=p*q。然后选择一个公钥指数e(通常是65537),计算出私钥d作为e的模反元素。加密时用公钥(n,e),解密时用私钥d。这个看似简单的数学过程,在实际题目中却可能隐藏着各种漏洞。

在CTF比赛中,RSA题目通常会给出部分参数,要求选手通过分析参数特性来恢复明文。常见的题型包括:给出p和q的直接解密、n较小可分解、共模攻击、小指数攻击等。下面我们就通过具体案例,来看看这些题型该如何破解。

2. 基础题型实战解析

2.1 标准RSA解密

最基础的RSA题目会直接给出p、q、e和密文c。这种情况相当于把解密所需的全部材料都给了你,只需要按照RSA的标准解密流程操作即可。

我遇到过这样一道题:

e = 65537 p = 104046835712664064779194734974271185635538927889880611929931939711001301561682270177931622974642789920918902563361293345434055764293612446888383912807143394009019803471816448923969637980671221111117965227402429634935481868701166522350570364727873283332371986860194245739423508566783663380619142431820861051179 q = 140171048074107988605773731671018901813928130582422889797732071529733091703843710859282267763783461738242958098610949120354497987945911021170842457552182880133642711307227072133812253341129830416158450499258216967879857581565380890788395068130033931180395926482431150295880926480086317733457392573931410220501 c = 4772758911204771028049020670778336799568778930072841084057809867608022732611295305096052430641881550781141776498904005589873830973301898523644744951545345404578466176725030290421649344936952480254902939417215148205735730754808467351639943474816280980230447097444682489223054499524197909719857300597157406075069204315022703894466226179507627070835428226086509767746759353822302809385047763292891543697277097068406512924796409393289982738071019047393972959228919115821862868057003145401072581115989680686073663259771587445250687060240991265143919857962047718344017741878925867800431556311785625469001771370852474292194

解题步骤如下:

  1. 计算n = p * q
  2. 计算φ(n) = (p-1)*(q-1)
  3. 计算d ≡ e⁻¹ mod φ(n)
  4. 解密m ≡ cᵈ mod n

Python实现代码:

import libnum from Crypto.Util.number import long_to_bytes # 给定参数 e = 65537 p = 104046835712664064779194734974271185635538927889880611929931939711001301561682270177931622974642789920918902563361293345434055764293612446888383912807143394009019803471816448923969637980671221111117965227402429634935481868701166522350570364727873283332371986860194245739423508566783663380619142431820861051179 q = 140171048074107988605773731671018901813928130582422889797732071529733091703843710859282267763783461738242958098610949120354497987945911021170842457552182880133642711307227072133812253341129830416158450499258216967879857581565380890788395068130033931180395926482431150295880926480086317733457392573931410220501 c = 4772758911204771028049020670778336799568778930072841084057809867608022732611295305096052430641881550781141776498904005589873830973301898523644744951545345404578466176725030290421649344936952480254902939417215148205735730754808467351639943474816280980230447097444682489223054499524197909719857300597157406075069204315022703894466226179507627070835428226086509767746759353822302809385047763292891543697277097068406512924796409393289982738071019047393972959228919115821862868057003145401072581115989680686073663259771587445250687060240991265143919857962047718344017741878925867800431556311785625469001771370852474292194 # 计算中间参数 n = p * q phi_n = (p-1)*(q-1) d = libnum.invmod(e, phi_n) # 解密 m = pow(c, d, n) print(long_to_bytes(m))

运行后会输出flag{b4by_R5A}。这类题目虽然简单,但却是理解RSA解密流程的基础。在实际比赛中,很多复杂的RSA题目都是在这个基础上增加各种变化。

2.2 小n分解攻击

当n比较小时(通常小于1024位),我们可以尝试直接分解n来获取p和q。我曾在一次比赛中遇到这样一道题:

e = 65537 n = 1455925529734358105461406532259911790807347616464991065301847 c = 69380371057914246192606760686152233225659503366319332065009

这里的n只有约80位,用现代计算机可以在短时间内分解。我们可以使用yafu这个强大的因数分解工具:

yafu-x64.exe factor(1455925529734358105461406532259911790807347616464991065301847)

很快就能得到分解结果:

P31 = 1212112637077862917192191913841 P31 = 1201147059438530786835365194567

有了p和q后,解密过程就和标准RSA一样了:

import libnum from Crypto.Util.number import long_to_bytes e = 65537 n = 1455925529734358105461406532259911790807347616464991065301847 c = 69380371057914246192606760686152233225659503366319332065009 # 分解得到的p和q p = 1212112637077862917192191913841 q = 1201147059438530786835365194567 # 标准解密流程 phi_n = (p-1)*(q-1) d = libnum.invmod(e, phi_n) m = pow(c, d, n) print(long_to_bytes(m))

输出结果为flag{fact0r_sma11_N}。这类题目提醒我们,在实际应用中,RSA的n必须足够大(现在推荐至少2048位),否则会被轻易破解。

3. 进阶攻击手法

3.1 共模攻击

当相同的明文用两组不同的公钥(e₁,n)和(e₂,n)加密,且e₁和e₂互质时,我们可以使用共模攻击恢复明文,而无需知道私钥d。这在CTF中是一个常见考点。

我遇到过这样一道题:

e1 = 797 n = 15944475431088053285580229796309956066521520107276817969079550919586650535459242543036143360865780730044733026945488511390818947440767542658956272380389388112372084760689777141392370253850735307578445988289714647332867935525010482197724228457592150184979819463711753058569520651205113690397003146105972408452854948512223702957303406577348717348753106868356995616116867724764276234391678899662774272419841876652126127684683752880568407605083606688884120054963974930757275913447908185712204577194274834368323239143008887554264746068337709465319106886618643849961551092377843184067217615903229068010117272834602469293571 c1 = 11157593264920825445770016357141996124368529899750745256684450189070288181107423044846165593218013465053839661401595417236657920874113839974471883493099846397002721270590059414981101686668721548330630468951353910564696445509556956955232059386625725883038103399028010566732074011325543650672982884236951904410141077728929261477083689095161596979213961494716637502980358298944316636829309169794324394742285175377601826473276006795072518510850734941703194417926566446980262512429590253643561098275852970461913026108090608491507300365391639081555316166526932233787566053827355349022396563769697278239577184503627244170930 e2 = 521 n = 15944475431088053285580229796309956066521520107276817969079550919586650535459242543036143360865780730044733026945488511390818947440767542658956272380389388112372084760689777141392370253850735307578445988289714647332867935525010482197724228457592150184979819463711753058569520651205113690397003146105972408452854948512223702957303406577348717348753106868356995616116867724764276234391678899662774272419841876652126127684683752880568407605083606688884120054963974930757275913447908185712204577194274834368323239143008887554264746068337709465319106886618643849961551092377843184067217615903229068010117272834602469293571 c2 = 6699274351853330023117840396450375948797682409595670560999898826038378040157859939888021861338431350172193961054314487476965030228381372659733197551597730394275360811462401853988404006922710039053586471244376282019487691307865741621991977539073601368892834227191286663809236586729196876277005838495318639365575638989137572792843310915220039476722684554553337116930323671829220528562573169295901496437858327730504992799753724465760161805820723578087668737581704682158991028502143744445435775458296907671407184921683317371216729214056381292474141668027801600327187443375858394577015394108813273774641427184411887546849

解题思路是利用扩展欧几里得算法找到满足e₁s₁ + e₂s₂ = 1的整数s₁和s₂,然后计算: m ≡ (c₁ˢ¹ * c₂ˢ²) mod n

Python实现:

import libnum from Crypto.Util.number import long_to_bytes def ext_euclid(a, b): if b == 0: return 1, 0, a else: x, y, q = ext_euclid(b, a % b) x, y = y, (x - (a // b) * y) return x, y, q e1 = 797 n = 15944475431088053285580229796309956066521520107276817969079550919586650535459242543036143360865780730044733026945488511390818947440767542658956272380389388112372084760689777141392370253850735307578445988289714647332867935525010482197724228457592150184979819463711753058569520651205113690397003146105972408452854948512223702957303406577348717348753106868356995616116867724764276234391678899662774272419841876652126127684683752880568407605083606688884120054963974930757275913447908185712204577194274834368323239143008887554264746068337709465319106886618643849961551092377843184067217615903229068010117272834602469293571 c1 = 11157593264920825445770016357141996124368529899750745256684450189070288181107423044846165593218013465053839661401595417236657920874113839974471883493099846397002721270590059414981101686668721548330630468951353910564696445509556956955232059386625725883038103399028010566732074011325543650672982884236951904410141077728929261477083689095161596979213961494716637502980358298944316636829309169794324394742285175377601826473276006795072518510850734941703194417926566446980262512429590253643561098275852970461913026108090608491507300365391639081555316166526932233787566053827355349022396563769697278239577184503627244170930 e2 = 521 c2 = 6699274351853330023117840396450375948797682409595670560999898826038378040157859939888021861338431350172193961054314487476965030228381372659733197551597730394275360811462401853988404006922710039053586471244376282019487691307865741621991977539073601368892834227191286663809236586729196876277005838495318639365575638989137572792843310915220039476722684554553337116930323671829220528562573169295901496437858327730504992799753724465760161805820723578087668737581704682158991028502143744445435775458296907671407184921683317371216729214056381292474141668027801600327187443375858394577015394108813273774641427184411887546849 # 确保e1和e2互质 assert libnum.gcd(e1, e2) == 1 # 扩展欧几里得算法求s1和s2 s1, s2, _ = ext_euclid(e1, e2) # 处理负数指数 if s1 < 0: s1 = -s1 c1 = libnum.invmod(c1, n) elif s2 < 0: s2 = -s2 c2 = libnum.invmod(c2, n) # 恢复明文 m = (pow(c1, s1, n) * pow(c2, s2, n)) % n print(long_to_bytes(m))

输出结果为flag{sh4r3_N}。这种攻击方式告诉我们,在实际应用中,即使使用不同的公钥指数加密相同消息,只要模数n相同且指数互质,就可能被破解。

3.2 小指数攻击

当加密指数e很小时(如e=3),如果明文m也很小,使得mᵉ < n,那么可以直接对c开e次方恢复明文,因为此时模运算没有起作用。

我遇到过这样一道题:

e = 3 n = 18970053728616609366458286067731288749022264959158403758357985915393383117963693827568809925770679353765624810804904382278845526498981422346319417938434861558291366738542079165169736232558687821709937346503480756281489775859439254614472425017554051177725143068122185961552670646275229009531528678548251873421076691650827507829859299300272683223959267661288601619845954466365134077547699819734465321345758416957265682175864227273506250707311775797983409090702086309946790711995796789417222274776215167450093735639202974148778183667502150202265175471213833685988445568819612085268917780718945472573765365588163945754761 c = 150409620528139732054476072280993764527079006992643377862720337847060335153837950368208902491767027770946661

解题思路是尝试找到k使得c + k*n是一个完全立方数:

import gmpy2 from Crypto.Util.number import long_to_bytes e = 3 n = gmpy2.mpz(18970053728616609366458286067731288749022264959158403758357985915393383117963693827568809925770679353765624810804904382278845526498981422346319417938434861558291366738542079165169736232558687821709937346503480756281489775859439254614472425017554051177725143068122185961552670646275229009531528678548251873421076691650827507829859299300272683223959267661288601619845954466365134077547699819734465321345758416957265682175864227273506250707311775797983409090702086309946790711995796789417222274776215167450093735639202974148778183667502150202265175471213833685988445568819612085268917780718945472573765365588163945754761) c = gmpy2.mpz(150409620528139732054476072280993764527079006992643377862720337847060335153837950368208902491767027770946661) i = 0 while True: root, is_success = gmpy2.iroot(c+i*n, e) if is_success: m = root break i += 1 print(long_to_bytes(m))

输出结果为flag{Sm4ll_eee}。这种攻击提醒我们,在实际应用中,不能使用太小的加密指数e,同时需要对明文进行适当的填充。

4. 特殊参数攻击

4.1 p和q接近时的攻击

当p和q非常接近时,n可以被快速分解。这是因为如果p和q接近,那么它们的差值很小,我们可以从√n开始向两边搜索附近的整数,很快就能找到p和q。

我遇到过这样一道题:

e = 0x10001 n = 26737417831000820542131903300607349805884383394154602685589253691058592906354935906805134188533804962897170211026684453428204518730064406526279112572388086653330354347467824800159214965211971007509161988095657918569122896402683130342348264873834798355125176339737540844380018932257326719850776549178097196650971801959829891897782953799819540258181186971887122329746532348310216818846497644520553218363336194855498009339838369114649453618101321999347367800581959933596734457081762378746706371599215668686459906553007018812297658015353803626409606707460210905216362646940355737679889912399014237502529373804288304270563 c = 18343406988553647441155363755415469675162952205929092244387144604220598930987120971635625205531679665588524624774972379282080365368504475385813836796957675346369136362299791881988434459126442243685599469468046961707420163849755187402196540739689823324440860766040276525600017446640429559755587590377841083082073283783044180553080312093936655426279610008234238497453986740658015049273023492032325305925499263982266317509342604959809805578180715819784421086649380350482836529047761222588878122181300629226379468397199620669975860711741390226214613560571952382040172091951384219283820044879575505273602318856695503917257

题目提示q是p的下一个素数。我们可以使用yafu分解n:

yafu-x64.exe factor(26737417831000820542131903300607349805884383394154602685589253691058592906354935906805134188533804962897170211026684453428204518730064406526279112572388086653330354347467824800159214965211971007509161988095657918569122896402683130342348264873834798355125176339737540844380018932257326719850776549178097196650971801959829891897782953799819540258181186971887122329746532348310216818846497644520553218363336194855498009339838369114649453618101321999347367800581959933596734457081762378746706371599215668686459906553007018812297658015353803626409606707460210905216362646940355737679889912399014237502529373804288304270563)

得到:

P309 = 163515803000813412334620775647541652549604895368507102613553057136855632963322853570924931001138446030409251690646645635800254129997200577719209532684847732809399187385176309169421205833279943214621695444496660249881675974141488357432373412184140130503562295159152949524373214358417567189638680209172147385801 P309 = 163515803000813412334620775647541652549604895368507102613553057136855632963322853570924931001138446030409251690646645635800254129997200577719209532684847732809399187385176309169421205833279943214621695444496660249881675974141488357432373412184140130503562295159152949524373214358417567189638680209172147385163

解密代码:

import libnum from Crypto.Util.number import long_to_bytes e = 0x10001 n = 26737417831000820542131903300607349805884383394154602685589253691058592906354935906805134188533804962897170211026684453428204518730064406526279112572388086653330354347467824800159214965211971007509161988095657918569122896402683130342348264873834798355125176339737540844380018932257326719850776549178097196650971801959829891897782953799819540258181186971887122329746532348310216818846497644520553218363336194855498009339838369114649453618101321999347367800581959933596734457081762378746706371599215668686459906553007018812297658015353803626409606707460210905216362646940355737679889912399014237502529373804288304270563 c = 18343406988553647441155363755415469675162952205929092244387144604220598930987120971635625205531679665588524624774972379282080365368504475385813836796957675346369136362299791881988434459126442243685599469468046961707420163849755187402196540739689823324440860766040276525600017446640429559755587590377841083082073283783044180553080312093936655426279610008234238497453986740658015049273023492032325305925499263982266317509342604959809805578180715819784421086649380350482836529047761222588878122181300629226379468397199620669975860711741390226214613560571952382040172091951384219283820044879575505273602318856695503917257 p = 163515803000813412334620775647541652549604895368507102613553057136855632963322853570924931001138446030409251690646645635800254129997200577719209532684847732809399187385176309169421205833279943214621695444496660249881675974141488357432373412184140130503562295159152949524373214358417567189638680209172147385801 q = 163515803000813412334620775647541652549604895368507102613553057136855632963322853570924931001138446030409251690646645635800254129997200577719209532684847732809399187385176309169421205833279943214621695444496660249881675974141488357432373412184140130503562295159152949524373214358417567189638680209172147385163 phi_n = (p-1)*(q-1) d = libnum.invmod(e, phi_n) m = pow(c, d, n) print(long_to_bytes(m))

输出结果为flag{p&q_4re_t00_c1o5ed}。这类题目提醒我们,在实际应用中,选择p和q时不仅要确保它们是随机的大素数,还要确保它们之间有足够的差距。

4.2 已知p高位攻击

当已知p的大部分高位时,可以使用Coppersmith定理恢复完整的p。这在CTF中也是一个常见考点。

我遇到过这样一道题:

e = 0x10001 n = 0x79e0bf9b916e59286163a1006f8cefd4c1b080387a6ddb98a3f3984569a4ebb48b22ac36dff7c98e4ebb90ffdd9c07f53a20946f57634fb01f4489fcfc8e402865e152820f3e2989d4f0b5ef1fb366f212e238881ea1da017f754d7840fc38236edba144674464b661d36cdaf52d1e5e7c3c21770c5461a7c1bc2db712a61d992ebc407738fc095cd8b6b64e7e532187b11bf78a8d3ddf52da6f6a67c7e88bef5563cac1e5ce115f3282d5ff9db02278859f63049d1b934d918f46353fea1651d96b2ddd874ec8f1e4b9d487d8849896d1c21fb64029f0d6f47e560555b009b96bfd558228929a6cdf3fb6d47a956829fb1e638fcc1bdfad4ec2c3590dea1ed3 c = 0x18343406988553647441155363755415469675162952205929092244387144604220598930987120971635625205531679665588524624774972379282080365368504475385813836796957675346369136362299791881988434459126442243685599469468046961707420163849755187402196540739689823324440860766040276525600017446640429559755587590377841083082073283783044180553080312093936655426279610008234238497453986740658015049273023492032325305925499263982266317509342604959809805578180715819784421086649380350482836529047761222588878122181300629226379468397199620669975860711741390226214613560571952382040172091951384219283820044879575505273602318856695503917257 p_fake = 0xd1c520d9798f811e87f4ff406941958bab8fc24b19a32c3ad89b0b73258ed3541e9ca696fd98ce15255264c39ae8c6e8db5ee89993fa44459410d30a0a8af700ae3aee8a9a1d6094f8c757d3b79a8d1147e85be34fb260a970a52826c0a92b46cefb5dfaf2b5a31edf867f8d34d2222900000000000000000000000000000000

我们可以使用SageMath的small_roots方法恢复完整的p:

# SageMath脚本 n = 0x79e0bf9b916e59286163a1006f8cefd4c1b080387a6ddb98a3f3984569a4ebb48b22ac36dff7c98e4ebb90ffdd9c07f53a20946f57634fb01f4489fcfc8e402865e152820f3e2989d4f0b5ef1fb366f212e238881ea1da017f754d7840fc38236edba144674464b661d36cdaf52d1e5e7c3c21770c5461a7c1bc2db712a61d992ebc407738fc095cd8b6b64e7e532187b11bf78a8d3ddf52da6f6a67c7e88bef5563cac1e5ce115f3282d5ff9db02278859f63049d1b934d918f46353fea1651d96b2ddd874ec8f1e4b9d487d8849896d1c21fb64029f0d6f47e560555b009b96bfd558228929a6cdf3fb6d47a956829fb1e638fcc1bdfad4ec2c3590dea1ed3 p_fake = 0xd1c520d9798f811e87f4ff406941958bab8fc24b19a32c3ad89b0b73258ed3541e9ca696fd98ce15255264c39ae8c6e8db5ee89993fa44459410d30a0a8af700ae3aee8a9a1d6094f8c757d3b79a8d1147e85be34fb260a970a52826c0a92b46cefb5dfaf2b5a31edf867f8d34d2222900000000000000000000000000000000 pbits = 1024 kbits = 128 pbar = p_fake & (2^pbits-2^kbits) PR.<x> = PolynomialRing(Zmod(n)) f = x + pbar x0 = f.small_roots(X=2^kbits, beta=0.4)[0] p = x0 + pbar print(p)

得到p后,就可以按标准流程解密:

import libnum from Crypto.Util.number import long_to_bytes e = 0x10001 n = 0x79e0bf9b916e59286163a1006f8cefd4c1b080387a6ddb98a3f3984569a4ebb48b22ac36dff7c98e4ebb90ffdd9c07f53a20946f57634fb01f4489fcfc8e402865e152820f3e2989d4f0b5ef1fb366f212e238881ea1da017f754d7840fc38236edba144674464b661d36cdaf52d1e5e7c3c21770c5461a7c1bc2db712a61d992ebc407738fc095cd8b6b64e7e532187b11bf78a8d3ddf52da6f6a67c7e88bef5563cac1e5ce115f3282d5ff9db02278859f63049d1b934d918f46353fea1651d96b2ddd874ec8f1e4b9d487d8849896d1c21fb64029f0d6f47e560555b009b96bfd558228929a6cdf3fb6d47a956829fb1e638fcc1bdfad4ec2c3590dea1ed3 c = 0x18343406988553647441155363755415469675162952205929092244387144604220598930987120971635625205531679665588524624774972379282080365368504475385813836796957675346369136362299791881988434459126442243685599469468046961707420163849755187402196540739689823324440860766040276525600017446640429559755587590377841083082073283783044180553080312093936655426279610008234238497453986740658015049273023492032325305925499263982266317509342604959809805578180715819784421086649380350482836529047761222588878122181300629226379468397199620669975860711741390226214613560571952382040172091951384219283820044879575505273602318856695503917257 p = 147305526294483975294006704928271118039370615054437206404408410848858740256154476278591035455064149531353089038270283281541411458250950936656537283482331598521457077465891874559349872035197398406708610440618635013091489698011474611145014167

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/1658690.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

新概念英语第一册119_A true story

Lesson 119: A true story Watch the story and answer the question Who called out to the thieves in the dark? 谁在暗处对窃贼喊了一声&#xff1f; George’s parrot, Henry.Key words and expressions happend 发生thief 贼&#xff08;thieves&#xff…

Nginx 学习总结涝

1. 引入 在现代 AI 工程中&#xff0c;Hugging Face 的 tokenizers 库已成为分词器的事实标准。不过 Hugging Face 的 tokenizers 是用 Rust 来实现的&#xff0c;官方只提供了 python 和 node 的绑定实现。要实现与 Hugging Face tokenizers 相同的行为&#xff0c;最好的办法…

从“看图说话”到“像素级理解”:细数多模态大模型(MLLM)在工业质检与自动驾驶中的真实落地案例

从“看图说话”到“像素级理解”&#xff1a;多模态大模型在工业质检与自动驾驶中的真实落地案例 当生产线上的电路板以每分钟200片的速度流过摄像头&#xff0c;传统视觉检测系统还在用预设规则判断焊点是否合格时&#xff0c;搭载Ferret模型的智能质检系统已经能根据工程师的…

Snyk 依赖性安全漏洞扫描工具实战指南:从安装到多语言项目扫描

1. Snyk工具与依赖安全漏洞扫描基础 第一次听说Snyk是在去年参与一个金融项目时&#xff0c;我们的技术负责人突然要求所有依赖包必须通过安全扫描才能上线。当时团队里没人知道该怎么操作&#xff0c;直到发现了这个神器。Snyk本质上是个"依赖包安检仪"&#xff0c;…

终极AssetStudio指南:轻松提取Unity游戏资源的完整教程

终极AssetStudio指南&#xff1a;轻松提取Unity游戏资源的完整教程 【免费下载链接】AssetStudio AssetStudio is an independent tool for exploring, extracting and exporting assets. 项目地址: https://gitcode.com/gh_mirrors/ass/AssetStudio &#x1f680; 你是…

新概念英语第一册117_Tommy s breakfast

Lesson 117: Tommy’s breakfast Watch the story and answer the question What does she mean by ‘change’ in the last sentence? Key words and expressions dining room 饭厅coin 硬币 note 纸币 mouth 嘴s…

[实践指南] 一致性正则化:从平滑假设到半监督学习实战

1. 一致性正则化&#xff1a;为什么我们需要它&#xff1f; 想象一下你在教一个小朋友识别动物。刚开始你给他看了10张猫和狗的照片&#xff0c;并告诉他哪些是猫、哪些是狗。过几天你发现&#xff0c;这个小朋友虽然能准确认出那10张照片&#xff0c;但遇到新的猫狗照片就完全…

新概念英语第一册115_Knock knock

Lesson 115: Knock, knock! Watch the story and answer the question What does Jim have to drink? 吉姆只能喝什么饮料&#xff1f; Key words and expressions knock v. 敲&#xff0c;打quiet 宁静的&#xff0c;安静的impossible 不可能的invite …
最新文章